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