KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tomcat > util > http > MimeHeaders


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.tomcat.util.http;
19
20 import java.io.PrintWriter JavaDoc;
21 import java.io.StringWriter JavaDoc;
22 import java.util.Enumeration JavaDoc;
23
24 import org.apache.tomcat.util.buf.MessageBytes;
25
26 /* XXX XXX XXX Need a major rewrite !!!!
27  */

28
29 /**
30  * This class is used to contain standard internet message headers,
31  * used for SMTP (RFC822) and HTTP (RFC2068) messages as well as for
32  * MIME (RFC 2045) applications such as transferring typed data and
33  * grouping related items in multipart message bodies.
34  *
35  * <P> Message headers, as specified in RFC822, include a field name
36  * and a field body. Order has no semantic significance, and several
37  * fields with the same name may exist. However, most fields do not
38  * (and should not) exist more than once in a header.
39  *
40  * <P> Many kinds of field body must conform to a specified syntax,
41  * including the standard parenthesized comment syntax. This class
42  * supports only two simple syntaxes, for dates and integers.
43  *
44  * <P> When processing headers, care must be taken to handle the case of
45  * multiple same-name fields correctly. The values of such fields are
46  * only available as strings. They may be accessed by index (treating
47  * the header as an array of fields), or by name (returning an array
48  * of string values).
49  */

50
51 /* Headers are first parsed and stored in the order they are
52    received. This is based on the fact that most servlets will not
53    directly access all headers, and most headers are single-valued.
54    ( the alternative - a hash or similar data structure - will add
55    an overhead that is not needed in most cases )
56    
57    Apache seems to be using a similar method for storing and manipulating
58    headers.
59        
60    Future enhancements:
61    - hash the headers the first time a header is requested ( i.e. if the
62    servlet needs direct access to headers).
63    - scan "common" values ( length, cookies, etc ) during the parse
64    ( addHeader hook )
65    
66 */

67     
68
69
70 /**
71  * Memory-efficient repository for Mime Headers. When the object is recycled, it
72  * will keep the allocated headers[] and all the MimeHeaderField - no GC is generated.
73  *
74  * For input headers it is possible to use the MessageByte for Fileds - so no GC
75  * will be generated.
76  *
77  * The only garbage is generated when using the String for header names/values -
78  * this can't be avoided when the servlet calls header methods, but is easy
79  * to avoid inside tomcat. The goal is to use _only_ MessageByte-based Fields,
80  * and reduce to 0 the memory overhead of tomcat.
81  *
82  * TODO:
83  * XXX one-buffer parsing - for http ( other protocols don't need that )
84  * XXX remove unused methods
85  * XXX External enumerations, with 0 GC.
86  * XXX use HeaderName ID
87  *
88  *
89  * @author dac@eng.sun.com
90  * @author James Todd [gonzo@eng.sun.com]
91  * @author Costin Manolache
92  * @author kevin seguin
93  */

94 public class MimeHeaders {
95     /** Initial size - should be == average number of headers per request
96      * XXX make it configurable ( fine-tuning of web-apps )
97      */

98     public static final int DEFAULT_HEADER_SIZE=8;
99     
100     /**
101      * The header fields.
102      */

103     private MimeHeaderField[] headers = new
104     MimeHeaderField[DEFAULT_HEADER_SIZE];
105
106     /**
107      * The current number of header fields.
108      */

109     private int count;
110
111     /**
112      * Creates a new MimeHeaders object using a default buffer size.
113      */

114     public MimeHeaders() {
115     }
116
117     /**
118      * Clears all header fields.
119      */

120     // [seguin] added for consistency -- most other objects have recycle().
121
public void recycle() {
122         clear();
123     }
124
125     /**
126      * Clears all header fields.
127      */

128     public void clear() {
129     for (int i = 0; i < count; i++) {
130         headers[i].recycle();
131     }
132     count = 0;
133     }
134
135     /**
136      * EXPENSIVE!!! only for debugging.
137      */

138     public String JavaDoc toString() {
139         StringWriter JavaDoc sw = new StringWriter JavaDoc();
140         PrintWriter JavaDoc pw = new PrintWriter JavaDoc(sw);
141         pw.println("=== MimeHeaders ===");
142         Enumeration JavaDoc e = names();
143         while (e.hasMoreElements()) {
144             String JavaDoc n = (String JavaDoc)e.nextElement();
145             pw.println(n + " = " + getHeader(n));
146         }
147         return sw.toString();
148     }
149
150     // -------------------- Idx access to headers ----------
151

152     /**
153      * Returns the current number of header fields.
154      */

155     public int size() {
156     return count;
157     }
158
159     /**
160      * Returns the Nth header name, or null if there is no such header.
161      * This may be used to iterate through all header fields.
162      */

163     public MessageBytes getName(int n) {
164     return n >= 0 && n < count ? headers[n].getName() : null;
165     }
166
167     /**
168      * Returns the Nth header value, or null if there is no such header.
169      * This may be used to iterate through all header fields.
170      */

171     public MessageBytes getValue(int n) {
172     return n >= 0 && n < count ? headers[n].getValue() : null;
173     }
174
175     /** Find the index of a header with the given name.
176      */

177     public int findHeader( String JavaDoc name, int starting ) {
178     // We can use a hash - but it's not clear how much
179
// benefit you can get - there is an overhead
180
// and the number of headers is small (4-5 ?)
181
// Another problem is that we'll pay the overhead
182
// of constructing the hashtable
183

184     // A custom search tree may be better
185
for (int i = starting; i < count; i++) {
186         if (headers[i].getName().equalsIgnoreCase(name)) {
187                 return i;
188             }
189         }
190         return -1;
191     }
192     
193     // -------------------- --------------------
194

195     /**
196      * Returns an enumeration of strings representing the header field names.
197      * Field names may appear multiple times in this enumeration, indicating
198      * that multiple fields with that name exist in this header.
199      */

200     public Enumeration JavaDoc names() {
201     return new NamesEnumerator(this);
202     }
203
204     public Enumeration JavaDoc values(String JavaDoc name) {
205     return new ValuesEnumerator(this, name);
206     }
207
208     // -------------------- Adding headers --------------------
209

210
211     /**
212      * Adds a partially constructed field to the header. This
213      * field has not had its name or value initialized.
214      */

215     private MimeHeaderField createHeader() {
216     MimeHeaderField mh;
217     int len = headers.length;
218     if (count >= len) {
219         // expand header list array
220
MimeHeaderField tmp[] = new MimeHeaderField[count * 2];
221         System.arraycopy(headers, 0, tmp, 0, len);
222         headers = tmp;
223     }
224     if ((mh = headers[count]) == null) {
225         headers[count] = mh = new MimeHeaderField();
226     }
227     count++;
228     return mh;
229     }
230
231     /** Create a new named header , return the MessageBytes
232     container for the new value
233     */

234     public MessageBytes addValue( String JavaDoc name ) {
235     MimeHeaderField mh = createHeader();
236     mh.getName().setString(name);
237     return mh.getValue();
238     }
239
240     /** Create a new named header using un-translated byte[].
241     The conversion to chars can be delayed until
242     encoding is known.
243      */

244     public MessageBytes addValue(byte b[], int startN, int len)
245     {
246     MimeHeaderField mhf=createHeader();
247     mhf.getName().setBytes(b, startN, len);
248     return mhf.getValue();
249     }
250
251     /** Create a new named header using translated char[].
252      */

253     public MessageBytes addValue(char c[], int startN, int len)
254     {
255     MimeHeaderField mhf=createHeader();
256     mhf.getName().setChars(c, startN, len);
257     return mhf.getValue();
258     }
259
260     /** Allow "set" operations -
261         return a MessageBytes container for the
262     header value ( existing header or new
263     if this .
264     */

265     public MessageBytes setValue( String JavaDoc name ) {
266         for ( int i = 0; i < count; i++ ) {
267             if(headers[i].getName().equalsIgnoreCase(name)) {
268                 for ( int j=i+1; j < count; j++ ) {
269                     if(headers[j].getName().equalsIgnoreCase(name)) {
270                         removeHeader(j--);
271                     }
272                 }
273                 return headers[i].getValue();
274             }
275         }
276         MimeHeaderField mh = createHeader();
277         mh.getName().setString(name);
278         return mh.getValue();
279     }
280
281     //-------------------- Getting headers --------------------
282
/**
283      * Finds and returns a header field with the given name. If no such
284      * field exists, null is returned. If more than one such field is
285      * in the header, an arbitrary one is returned.
286      */

287     public MessageBytes getValue(String JavaDoc name) {
288         for (int i = 0; i < count; i++) {
289         if (headers[i].getName().equalsIgnoreCase(name)) {
290                 return headers[i].getValue();
291             }
292         }
293         return null;
294     }
295
296     // bad shortcut - it'll convert to string ( too early probably,
297
// encoding is guessed very late )
298
public String JavaDoc getHeader(String JavaDoc name) {
299     MessageBytes mh = getValue(name);
300     return mh != null ? mh.toString() : null;
301     }
302
303     // -------------------- Removing --------------------
304
/**
305      * Removes a header field with the specified name. Does nothing
306      * if such a field could not be found.
307      * @param name the name of the header field to be removed
308      */

309     public void removeHeader(String JavaDoc name) {
310         // XXX
311
// warning: rather sticky code; heavily tuned
312

313         for (int i = 0; i < count; i++) {
314             if (headers[i].getName().equalsIgnoreCase(name)) {
315                 removeHeader(i--);
316             }
317         }
318     }
319
320     /**
321      * reset and swap with last header
322      * @param idx the index of the header to remove.
323      */

324     private void removeHeader(int idx) {
325         MimeHeaderField mh = headers[idx];
326         
327         mh.recycle();
328         headers[idx] = headers[count - 1];
329         headers[count - 1] = mh;
330         count--;
331     }
332
333 }
334
335 /** Enumerate the distinct header names.
336     Each nextElement() is O(n) ( a comparation is
337     done with all previous elements ).
338
339     This is less frequesnt than add() -
340     we want to keep add O(1).
341 */

342 class NamesEnumerator implements Enumeration JavaDoc {
343     int pos;
344     int size;
345     String JavaDoc next;
346     MimeHeaders headers;
347
348     NamesEnumerator(MimeHeaders headers) {
349     this.headers=headers;
350     pos=0;
351     size = headers.size();
352     findNext();
353     }
354
355     private void findNext() {
356     next=null;
357     for( ; pos< size; pos++ ) {
358         next=headers.getName( pos ).toString();
359         for( int j=0; j<pos ; j++ ) {
360         if( headers.getName( j ).equalsIgnoreCase( next )) {
361             // duplicate.
362
next=null;
363             break;
364         }
365         }
366         if( next!=null ) {
367         // it's not a duplicate
368
break;
369         }
370     }
371     // next time findNext is called it will try the
372
// next element
373
pos++;
374     }
375     
376     public boolean hasMoreElements() {
377     return next!=null;
378     }
379
380     public Object JavaDoc nextElement() {
381     String JavaDoc current=next;
382     findNext();
383     return current;
384     }
385 }
386
387 /** Enumerate the values for a (possibly ) multiple
388     value element.
389 */

390 class ValuesEnumerator implements Enumeration JavaDoc {
391     int pos;
392     int size;
393     MessageBytes next;
394     MimeHeaders headers;
395     String JavaDoc name;
396
397     ValuesEnumerator(MimeHeaders headers, String JavaDoc name) {
398         this.name=name;
399     this.headers=headers;
400     pos=0;
401     size = headers.size();
402     findNext();
403     }
404
405     private void findNext() {
406     next=null;
407     for( ; pos< size; pos++ ) {
408         MessageBytes n1=headers.getName( pos );
409         if( n1.equalsIgnoreCase( name )) {
410         next=headers.getValue( pos );
411         break;
412         }
413     }
414     pos++;
415     }
416     
417     public boolean hasMoreElements() {
418     return next!=null;
419     }
420
421     public Object JavaDoc nextElement() {
422     MessageBytes current=next;
423     findNext();
424     return current.toString();
425     }
426 }
427
428 class MimeHeaderField {
429     // multiple headers with same name - a linked list will
430
// speed up name enumerations and search ( both cpu and
431
// GC)
432
MimeHeaderField next;
433     MimeHeaderField prev;
434     
435     protected final MessageBytes nameB = MessageBytes.newInstance();
436     protected final MessageBytes valueB = MessageBytes.newInstance();
437
438     /**
439      * Creates a new, uninitialized header field.
440      */

441     public MimeHeaderField() {
442     }
443
444     public void recycle() {
445     nameB.recycle();
446     valueB.recycle();
447     next=null;
448     }
449
450     public MessageBytes getName() {
451     return nameB;
452     }
453
454     public MessageBytes getValue() {
455     return valueB;
456     }
457 }
458
Popular Tags