001: /*
002: * RequestHeader.java February 2001
003: *
004: * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013: * GNU Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General
016: * Public License along with this library; if not, write to the
017: * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
018: * Boston, MA 02111-1307 USA
019: */
020:
021: package simple.http;
022:
023: import simple.util.parse.DateParser;
024: import simple.util.parse.ListParser;
025: import simple.util.ByteStore;
026:
027: /**
028: * This is a <code>RequestHeader</code> object that can be used to
029: * manipulate data about a HTTP request header. This provides some
030: * convinence methods that allow the HTTP request to be manipulated
031: * easily.
032: * <p>
033: * This implements the <code>RequestLine</code> interface which
034: * provides methods specific to the HTTP request line header.
035: * The <code>RequestLine</code> also contains information on the
036: * HTTP version used with this request.
037: *
038: * @author Niall Gallagher
039: */
040: abstract class RequestHeader implements Request {
041:
042: /**
043: * This is used to parse the message header.
044: */
045: private HeaderParser parser;
046:
047: /**
048: * This is used to parse the request line.
049: */
050: private RequestParser req;
051:
052: /**
053: * This is used to parse any date headers.
054: */
055: private DateParser date;
056:
057: /**
058: * Provides a quick search and retrevial.
059: */
060: private HeaderList list;
061:
062: /**
063: * The URI that this request refers to.
064: */
065: private String target;
066:
067: /**
068: * The HTTP method of this request header.
069: */
070: private String method;
071:
072: /**
073: * The major version number of this request.
074: */
075: private int major = -1;
076:
077: /**
078: * The minor version number of this request.
079: */
080: private int minor = -1;
081:
082: /**
083: * This creates a <code>RequestHeader</code> object that can be
084: * used to manipulate data of a HTTP <code>RequestHeader</code>.
085: * The buffer that is used contains the bytes that form the
086: * HTTP <code>RequestHeader</code>.
087: *
088: * @param buf the <code>RequestHeader</code> bytes.
089: */
090: protected RequestHeader(ByteStore buf) {
091: this .req = new RequestParser(buf);
092: this .parser = new HeaderParser(buf);
093: this .list = new HeaderList();
094: this .date = new DateParser();
095: this .init();
096: }
097:
098: /**
099: * This is used to initialize the object by parsing the HTTP
100: * message header and building the <code>HeaderList</code>.
101: * This ensures that the the headers can be accessed.
102: */
103: private void init() {
104: while (parser.hasMore()) {
105: list.add(parser.next());
106: }
107: }
108:
109: /**
110: * This can be used to get the URI specified for this HTTP
111: * request. This corrosponds to the /index part of a
112: * http://www.domain.com/index URL but may contain the full
113: * URL. This can be set using <code>setURI</code>.
114: *
115: * @return the URI that this HTTP request is targeting
116: */
117: public String getURI() {
118: if (target != null) {
119: return target;
120: }
121: target = req.getURI();
122: return target;
123: }
124:
125: /**
126: * This can be used to set the URI for this HTTP request.
127: * The <code>getURI</code> will return the String entered
128: * which can be a full HTTP URL or a relative path URL.
129: *
130: * @param target the URI that this HTTP request is to use
131: */
132: public void setURI(String target) {
133: this .target = target;
134: }
135:
136: /**
137: * This can be used to get the HTTP method for this
138: * request. The HTTP specification RFC 2616 specifies the
139: * HTTP request methods in section 9, Method Definitions.
140: *
141: * @return the request method for this request
142: */
143: public String getMethod() {
144: if (method != null) {
145: return method;
146: }
147: method = req.getMethod();
148: return method;
149: }
150:
151: /**
152: * This is used to set the method for this HTTP request
153: * object. For a list of possible string values that
154: * can be used see RFC 2616 section 9, Method Definitions.
155: *
156: * @param method the desired method for this
157: */
158: public void setMethod(String method) {
159: this .method = method;
160: }
161:
162: /**
163: * This can be used to get the major number from a HTTP
164: * version. The major version corrosponds to the major
165: * type that is the 1 of a HTTP/1.0 version string.
166: *
167: * @return the major version number for the request
168: */
169: public int getMajor() {
170: if (major != -1) {
171: return major;
172: }
173: major = req.getMajor();
174: return major;
175: }
176:
177: /**
178: * This can be used to specify the major version for the
179: * HTTP request. Specifying the major version has little
180: * effect on the semantics of the request.
181: *
182: * @param major this is the major number desired
183: */
184: public void setMajor(int major) {
185: this .major = major;
186: }
187:
188: /**
189: * This can be used to get the major number from a HTTP
190: * version. The major version corrosponds to the major
191: * type that is the 0 of a HTTP/1.0 version string.
192: *
193: * @return the major version number
194: */
195: public int getMinor() {
196: if (minor != -1) {
197: return minor;
198: }
199: minor = req.getMinor();
200: return minor;
201: }
202:
203: /**
204: * This can be used to specify the minor version for the
205: * HTTP request. Specifying the minor version will effect
206: * the manner in which the request is processed.
207: *
208: * @param minor this is the minor number desired
209: */
210: public void setMinor(int minor) {
211: this .minor = minor;
212: }
213:
214: /**
215: * This can be used to determine how many HTTP message headers
216: * this object contains. The <code>headerCount</code> represents
217: * the number of individual HTTP message headers that this has.
218: *
219: * @return returns the number of HTTP message headers this has
220: */
221: public int headerCount() {
222: return list.headerCount();
223: }
224:
225: /**
226: * This can be used to add a HTTP message header to this object.
227: * The name and value of the HTTP message header will be used to
228: * create a HTTP message header object which can be retrived using
229: * the <code>indexOf</code> in combination with the get methods.
230: *
231: * @param name the name of the HTTP message header to be added
232: * @param val the value the HTTP message header will have
233: */
234: public void add(String name, String val) {
235: list.add(new PlainHeader(name, val));
236: }
237:
238: /**
239: * This can be used to set a HTTP message header to this object.
240: * The name and value of the HTTP message header will be used to
241: * create a HTTP message header object which can be retrived using
242: * the <code>indexOf</code> in combination with the get methods.
243: * This will perform a <code>removeAll</code> using the issued
244: * header name before the header value is set.
245: *
246: * @param name the name of the HTTP message header to be added
247: * @param val the value the HTTP message header will have
248: */
249: public void set(String name, String val) {
250: removeAll(name);
251: add(name, val);
252: }
253:
254: /**
255: * This can be used to add a HTTP message header to this object.
256: * The name and value of the HTTP message header will be used to
257: * create a HTTP message header object which can be retrived using
258: * the <code>indexOf</code> in combination with the get methods.
259: *
260: * @param name the name of the HTTP message header to be added
261: * @param val the value the HTTP message header will have
262: */
263: public void add(String name, int val) {
264: add(name, String.valueOf(val));
265: }
266:
267: /**
268: * This can be used to set a HTTP message header to this object.
269: * The name and value of the HTTP message header will be used to
270: * create a HTTP message header object which can be retrived using
271: * the <code>indexOf</code> in combination with the get methods.
272: * This will perform a <code>removeAll</code> using the issued
273: * header name before the header value is set.
274: *
275: * @param name the name of the HTTP message header to be added
276: * @param val the value the HTTP message header will have
277: */
278: public void set(String name, int val) {
279: removeAll(name);
280: add(name, val);
281: }
282:
283: /**
284: * This can be used to get the value of the HTTP message header
285: * at the specified index. This is a convinence method that
286: * avoids having to deal with a HTTP message header object. If
287: * the offset used specified is invalid then an exception may be
288: * thrown.
289: *
290: * @param off the offset of the HTTP message header value to be
291: * returned
292: *
293: * @return this returns the value that the HTTP message header
294: */
295: public String getValue(int off) {
296: return list.getValue(off);
297: }
298:
299: /**
300: * This is used to get the name value of the HTTP message header
301: * at the specified index. This is used in conjunction with the
302: * <code>getValue(int)</code> method so that the contents of the
303: * HTTP message header can be fully examined.
304: *
305: * @param off the offset of the HTTP message header name value
306: *
307: * @return this returns the name of the header at that index
308: */
309: public String getName(int off) {
310: return list.getName(off);
311: }
312:
313: /**
314: * This is used as a convienence method for adding a header that
315: * needs to be parsed into a HTTP-date string. This will convert
316: * the date given into a date string defined in RFC 2616 sec 3.3.1.
317: *
318: * @param name the name of the HTTP message header to be added
319: * @param time the value the HTTP message header will have when
320: * parsed into RFC 1123 format
321: */
322: public void addDate(String name, long time) {
323: date.parse(time);
324: add(name, date.toString());
325: }
326:
327: /**
328: * This is used as a convienence method for setting a header that
329: * needs to be parsed into a HTTP-date string. This will convert
330: * the date given into a date string defined in RFC 2616 sec 3.3.1.
331: * This will perform a <code>removeAll</code> using the issued
332: * header name before the header value is set.
333: *
334: * @param name the name of the HTTP message header to be added
335: * @param time the value the HTTP message header will have when
336: * parsed into RFC 1123 format
337: */
338: public void setDate(String name, long time) {
339: removeAll(name);
340: addDate(name, time);
341: }
342:
343: /**
344: * This can be used to get the value of the HTTP message header
345: * at the specified index. This is a convinence method that
346: * avoids having to deal with a HTTP message header object. If
347: * the offset used specified is invalid then an exception may be
348: * thrown.
349: *
350: * @param off the offset of the HTTP message header value to be
351: * returned
352: *
353: * @return this returns the date as a long from the parsed value
354: * of that HTTP message header
355: */
356: public long getDate(int off) {
357: String value = getValue(off);
358: if (value == null) {
359: return -1;
360: }
361: date.parse(value);
362: return date.toLong();
363: }
364:
365: /**
366: * This can be used to get the date of the first HTTP message header
367: * that has the specified name. This is a convinence method that
368: * avoids having to deal with parsing the value of the requested
369: * HTTP message header. This also avoids having to deal with the
370: * <code>indexOf</code> methods. This returns -1 if theres not a
371: * HTTP message header.
372: *
373: * @param name the HTTP message header to get the value from
374: *
375: * @return this returns the date as a long from the parsed value
376: * of that HTTP message header
377: */
378: public long getDate(String name) {
379: String value = getValue(name);
380: if (value == null) {
381: return -1;
382: }
383: date.parse(value);
384: return date.toLong();
385: }
386:
387: /**
388: * This can be used to find the first occurence of the specified
389: * HTTP message header. This will search through the list of
390: * HTTP message headers that this contains and when it encounters
391: * a HTTP message header with the name specified it returns the
392: * index of that HTTP message header. The index will change when
393: * a remove is used. So the index is valid only for the until the
394: * next remove method or possible the next add method.
395: *
396: * @param name name of the HTTP message header being searched for
397: *
398: * @return returns the position of the first HTTP message header
399: */
400: public int indexOf(String name) {
401: return list.indexOf(name);
402: }
403:
404: /**
405: * This can be used to find the first occurence of the specified
406: * HTTP message header from a given index. This will search through
407: * the list of HTTP message headers that occur after the index.
408: * When it encounters a HTTP message header with the name specified
409: * it returns the index of that HTTP message header. The index will
410: * change when a remove is used. So the index is valid only until a
411: * remove or add method is used.
412: *
413: * @param name name of the HTTP message header being searched for
414: * @param from the index from which the search will start
415: *
416: * @return this returns the position of the HTTP message header
417: */
418: public int indexOf(String name, int from) {
419: return list.indexOf(name, from);
420: }
421:
422: /**
423: * This can be used to remove the HTTP message header at the
424: * specified index. This will invalidate any value recived by
425: * an <code>indexOf</code> method previous to this. If the
426: * index specified is not valid then an
427: * <code>IndexOutOfBoundsException</code> may be thrown.
428: *
429: * @param off index of the HTTP message header to be removed
430: */
431: public void remove(int off) {
432: list.remove(off);
433: }
434:
435: /**
436: * This can be used to remove all HTTP message headers with
437: * the specified name. This will search through the list of
438: * HTTP message header an remove the HTTP message headers
439: * from the list. This will invalidate any previous indexes
440: * recived.
441: *
442: * @param name name of the HTTP message headers to be removed
443: */
444: public void removeAll(String name) {
445: list.removeAll(name);
446: }
447:
448: /**
449: * This can be used to get the value of the first HTTP message header
450: * that has the specified name. This is a convinence method that
451: * avoids having to deal with a HTTP message header object and
452: * the <code>indexOf</code> methods. this returns null if theres
453: * not HTTP message header.
454: *
455: * @param name the HTTP message header to get the value from
456: *
457: * @return this returns the value that the HTTP message header
458: */
459: public String getValue(String name) {
460: return list.getValue(name);
461: }
462:
463: /**
464: * This can be used to get the values of HTTP message headers
465: * that have the specified name. This is a convenience method that
466: * will present the values as tokens extracted from the header.
467: * This has obvious performance benifits as it avoids having to
468: * deal with <code>substring</code> and <code>trim</code> calls.
469: * <p>
470: * The tokens returned by this method are ordered according to
471: * there HTTP quality values, or "q" values, see RFC 2616 section
472: * 3.9. This also strips out the quality parameter from tokens
473: * returned. So "image/html; q=0.9" results in "image/html". If
474: * there are no "q" values present then order is preserved.
475: *
476: * @param name the name of the headers that are to be retrieved
477: *
478: * @return ordered array of tokens extracted from the header(s)
479: */
480: public String[] getValues(String name) {
481: return getValues(list.getValues(name));
482: }
483:
484: /**
485: * This method will parse the given list of HTTP header values
486: * into usable tokens. Although the conversion from the complete
487: * HTTP header values to descrete tokens is costly the overall
488: * benifit to performance is obvious.
489: * <p>
490: * Rather that calling the <code>getValue(String)</code> method
491: * and parsing a token with the <code>String.substring</code>
492: * or <code>String.trim</code> methods the tokens can be used
493: * immediately, this avoids errors and overheads.
494: *
495: * @param list this is the list of HTTP header values to parse
496: *
497: * @return this wll return an ordered list of extracted tokens
498: */
499: private String[] getValues(String[] list) {
500: return new ListParser(list).list();
501: }
502:
503: /**
504: * This is used to see if there is a HTTP message header with the
505: * given name in this container. If there is a HTTP message header
506: * with the specified name then this returns true otherwize false.
507: *
508: * @param name the HTTP message header to get the value from
509: *
510: * @return this returns true if the HTTP message header exists
511: */
512: public boolean contains(String name) {
513: return indexOf(name) != -1;
514: }
515:
516: /**
517: * This is used to see if there is a HTTP message header with the
518: * given name in this container, if it exists this will check to
519: * see if the provided value exists. This is used for a comma
520: * seperated list of values found within the HTTP header value.
521: * If the header and token exits this returns true otherwise false.
522: *
523: * @param name the HTTP message header to get the value from
524: * @param value this value to find within the HTTP value
525: *
526: * @return this returns true if the HTTP message value exists
527: */
528: public boolean contains(String name, String value) {
529: String[] list = getValues(name);
530:
531: for (int i = 0; i < list.length; i++) {
532: if (list[i].equalsIgnoreCase(value))
533: return true;
534: }
535: return false;
536: }
537:
538: /**
539: * This is used to clear all HTTP message headers from the
540: * message header. This will leave no data remaining, i.e.
541: * <code>headerCount</code> is zero after this method is
542: * invoked, this is a convienence method.
543: */
544: public void clear() {
545: while (headerCount() > 0) {
546: remove(0);
547: }
548: }
549:
550: /**
551: * This method is used so that the original HTTP header can
552: * be reconstructed This returns a <code>String</code> that
553: * contains each header formatted according to the HTTP/1.1
554: * header format. The header will contain the request line
555: * followed by each header and ended with the CRLF.
556: *
557: * @return the HTTP request header as a sring object
558: */
559: public String toString() {
560: int size = headerCount() * 50;
561: StringBuffer buf = new StringBuffer(size);
562: buf.append(getMethod());
563: buf.append(" ").append(getURI());
564: buf.append(" HTTP/");
565: buf.append(getMajor()).append(".");
566: buf.append(getMinor()).append("\r\n");
567: for (int i = 0, len = headerCount(); i < len; i++) {
568: Header head = list.get(i);
569: buf.append(head.getName());
570: buf.append(": ");
571: buf.append(head.getValue());
572: buf.append("\r\n");
573: }
574: buf.append("\r\n");
575: return buf.toString();
576: }
577: }
|