1/*
2 * Copyright 2008 Netflix, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package net.oauth.http;
18
19import java.io.IOException;
20import java.io.InputStream;
21import java.net.URL;
22import java.util.ArrayList;
23import java.util.Iterator;
24import java.util.List;
25import java.util.Map;
26import java.util.regex.Matcher;
27import java.util.regex.Pattern;
28import net.oauth.client.ExcerptInputStream;
29
30/**
31 * An HTTP request or response.
32 *
33 * @author John Kristian
34 * @hide
35 */
36public class HttpMessage
37{
38
39    public HttpMessage()
40    {
41        this(null, null);
42    }
43
44    public HttpMessage(String method, URL url)
45    {
46        this(method, url, null);
47    }
48
49    public HttpMessage(String method, URL url, InputStream body)
50    {
51        this.method = method;
52        this.url = url;
53        this.body = body;
54    }
55
56    public String method;
57    public URL url;
58    public final List<Map.Entry<String, String>> headers = new ArrayList<Map.Entry<String, String>>();
59    protected InputStream body = null;
60
61    /**
62     * Get the value of the last header of the given name. The name is
63     * case-insensitive.
64     */
65    public final String getHeader(String name)
66    {
67        String value = null;
68        for (Map.Entry<String, String> header : headers) {
69            if (equalsIgnoreCase(name, header.getKey())) {
70                value = header.getValue();
71            }
72        }
73        return value;
74    }
75
76    /**
77     * Remove all headers of the given name. The name is case insensitive.
78     *
79     * @return the value of the last header with that name, or null to indicate
80     *         there was no such header
81     */
82    public String removeHeaders(String name)
83    {
84        String value = null;
85        for (Iterator<Map.Entry<String, String>> i = headers.iterator(); i.hasNext();) {
86            Map.Entry<String, String> header = i.next();
87            if (equalsIgnoreCase(name, header.getKey())) {
88                value = header.getValue();
89                i.remove();
90            }
91        }
92        return value;
93    }
94
95    public final String getContentCharset()
96    {
97        return getCharset(getHeader(CONTENT_TYPE));
98    }
99
100    public final InputStream getBody() throws IOException
101    {
102        if (body == null) {
103            InputStream raw = openBody();
104            if (raw != null) {
105                body = new ExcerptInputStream(raw);
106            }
107        }
108        return body;
109    }
110
111    protected InputStream openBody() throws IOException
112    {
113        return null;
114    }
115
116    /** Put a description of this message and its origins into the given Map. */
117    public void dump(Map<String, Object> into) throws IOException
118    {
119    }
120
121    private static boolean equalsIgnoreCase(String x, String y)
122    {
123        if (x == null)
124            return y == null;
125        else
126            return x.equalsIgnoreCase(y);
127    }
128
129    private static final String getCharset(String mimeType)
130    {
131        if (mimeType != null) {
132            Matcher m = CHARSET.matcher(mimeType);
133            if (m.find()) {
134                String charset = m.group(1);
135                if (charset.length() >= 2 && charset.charAt(0) == '"'
136                        && charset.charAt(charset.length() - 1) == '"') {
137                    charset = charset.substring(1, charset.length() - 1);
138                    charset = charset.replace("\\\"", "\"");
139                }
140                return charset;
141            }
142        }
143        return DEFAULT_CHARSET;
144    }
145
146    /** The name of a dump entry whose value is the HTTP request. */
147    public static final String REQUEST = "HTTP request";
148
149    /** The name of a dump entry whose value is the HTTP response. */
150    public static final String RESPONSE = "HTTP response";
151
152    public static final String ACCEPT_ENCODING = "Accept-Encoding";
153    public static final String CONTENT_ENCODING = "Content-Encoding";
154    public static final String CONTENT_LENGTH = "Content-Length";
155    public static final String CONTENT_TYPE = "Content-Type";
156    public static final String DEFAULT_CHARSET = "ISO-8859-1";
157
158    private static final Pattern CHARSET = Pattern
159            .compile("; *charset *= *([^;\"]*|\"([^\"]|\\\\\")*\")(;|$)");
160
161}
162