OSDN Git Service

改行コード指定
[jindolf/Jindolf.git] / src / main / java / jp / sourceforge / jindolf / HttpUtils.java
1 /*
2  * HTTP utilities
3  *
4  * License : The MIT License
5  * Copyright(c) 2008 olyutorskii
6  */
7
8 package jp.sourceforge.jindolf;
9
10 import java.io.IOException;
11 import java.net.HttpURLConnection;
12 import java.net.URLConnection;
13 import java.text.NumberFormat;
14 import java.util.regex.Matcher;
15 import java.util.regex.Pattern;
16
17 /**
18  * HTTP関連のユーティリティ群。
19  */
20 public final class HttpUtils{
21
22     private static final String TOKEN_REGEX =
23             "([^\\(\\)<>@,;:\\\"/\\[\\]\\?=\\{\\}\\p{Blank}\\p{Cntrl}]+)";
24     private static final String MTYPE_REGEX =
25             "[\\p{Blank}]*"
26             + TOKEN_REGEX + "/" + TOKEN_REGEX
27             + "[\\p{Blank}]*";
28     private static final String PARAM_REGEX =
29             "[\\p{Blank}]*;[\\p{Blank}]*"
30             + TOKEN_REGEX
31             + "[\\p{Blank}]*=[\\p{Blank}]*"
32             + "(" + TOKEN_REGEX + "|" + "(\"[^\\p{Cntrl}\\\"]*\")" + ")";
33     private static final Pattern MTYPE_PATTERN = Pattern.compile(MTYPE_REGEX);
34     private static final Pattern ATTR_PATTERN  = Pattern.compile(PARAM_REGEX);
35
36     private static final NumberFormat THROUGHPUT_FORMAT;
37     private static final NumberFormat SIZE_FORMAT;
38
39     static{
40         THROUGHPUT_FORMAT = NumberFormat.getInstance();
41         THROUGHPUT_FORMAT.setMaximumFractionDigits(1);
42         THROUGHPUT_FORMAT.setMinimumFractionDigits(1);
43         THROUGHPUT_FORMAT.setGroupingUsed(true);
44         SIZE_FORMAT = NumberFormat.getInstance();
45         SIZE_FORMAT.setGroupingUsed(true);
46     }
47
48
49     /**
50      * 隠れコンストラクタ。
51      */
52     private HttpUtils(){
53         super();
54         assert false;
55         throw new AssertionError();
56     }
57
58
59     /**
60      * ネットワークのスループット報告用文字列を生成する。
61      * @param size 転送サイズ(バイト数)
62      * @param nano 所要時間(ナノ秒)
63      * @return スループット文字列
64      */
65     public static String throughput(long size, long nano){
66         if(size <= 0 || nano <= 0) return "";
67
68         double sec = ((double)nano) / (1000.0 * 1000.0 * 1000.0);
69         double rate = ((double)size) / sec;
70
71         String unit = "";
72         if(rate >= 1500.0){
73             rate /= 1000.0;
74             unit = "K";
75         }
76         if(rate >= 1500.0){
77             rate /= 1000.0;
78             unit = "M";
79         }
80
81         String result =  SIZE_FORMAT.format(size) + "Bytes "
82                        + THROUGHPUT_FORMAT.format(rate) + unit
83                        + "Bytes/sec";
84         return result;
85     }
86
87     /**
88      * HTTPセッションの各種結果を文字列化する。
89      * @param conn HTTPコネクション
90      * @param size 転送サイズ
91      * @param nano 転送に要したナノ秒
92      * @return セッション結果
93      */
94     public static String formatHttpStat(HttpURLConnection conn,
95                                           long size,
96                                           long nano ){
97         String method = conn.getRequestMethod();
98         String url    = conn.getURL().toString();
99
100         String responseCode;
101         try{
102             responseCode = String.valueOf(conn.getResponseCode());
103         }catch(IOException e){
104             responseCode = "???";
105         }
106
107         String responseMessage;
108         try{
109             responseMessage = conn.getResponseMessage();
110         }catch(IOException e){
111             responseMessage = "???";
112         }
113
114         String throughput = throughput(size, nano);
115
116         String message =  method
117                         + " " + url
118                         + " [" + responseCode
119                         + " " + responseMessage + "]"
120                         + " " + throughput;
121
122         return message;
123     }
124
125     /**
126      * ユーザエージェント名を返す。
127      * @return ユーザエージェント名
128      */
129     public static String getUserAgentName(){
130         StringBuilder result = new StringBuilder();
131         result.append(Jindolf.TITLE).append("/").append(Jindolf.VERSION);
132
133         StringBuilder rawComment = new StringBuilder();
134         if(EnvInfo.OS_NAME != null){
135             if(rawComment.length() > 0) rawComment.append("; ");
136             rawComment.append(EnvInfo.OS_NAME);
137         }
138         if(EnvInfo.OS_VERSION != null){
139             if(rawComment.length() > 0) rawComment.append("; ");
140             rawComment.append(EnvInfo.OS_VERSION);
141         }
142         if(EnvInfo.OS_ARCH != null){
143             if(rawComment.length() > 0) rawComment.append("; ");
144             rawComment.append(EnvInfo.OS_ARCH);
145         }
146         if(EnvInfo.JAVA_VENDOR != null){
147             if(rawComment.length() > 0) rawComment.append("; ");
148             rawComment.append(EnvInfo.JAVA_VENDOR);
149         }
150         if(EnvInfo.JAVA_VERSION != null){
151             if(rawComment.length() > 0) rawComment.append("; ");
152             rawComment.append(EnvInfo.JAVA_VERSION);
153         }
154
155         CharSequence comment = escapeHttpComment(rawComment);
156         if(comment != null) result.append(" ").append(comment);
157
158         return result.toString();
159     }
160
161     /**
162      * 与えられた文字列からHTTPコメントを生成する。
163      * @param comment コメント
164      * @return HTTPコメント
165      */
166     public static String escapeHttpComment(CharSequence comment){
167         if(comment == null) return null;
168         if(comment.length() <= 0) return null;
169
170         String result = comment.toString();
171         result = result.replaceAll("\\(", "\\\\(");
172         result = result.replaceAll("\\)", "\\\\)");
173         result = result.replaceAll("[\\u0000-\\u001f]", "?");
174         result = result.replaceAll("[\\u007f-\\uffff]", "?");
175         result = "(" + result + ")";
176
177         return result;
178     }
179
180     /**
181      * HTTP応答からCharsetを取得する。
182      * @param connection HTTP接続
183      * @return Charset文字列
184      */
185     public static String getHTMLCharset(URLConnection connection){
186         String contentType = connection.getContentType();
187         if(contentType == null) return null;
188         return getHTMLCharset(contentType);
189     }
190
191     /**
192      * ContentTypeからCharsetを取得する。
193      * @param contentType ContentType
194      * @return Charset文字列
195      */
196     public static String getHTMLCharset(String contentType){
197         Matcher matcher;
198         boolean matchResult;
199         int lastPos;
200
201         matcher = MTYPE_PATTERN.matcher(contentType);
202         matchResult = matcher.lookingAt();
203         if(!matchResult) return null;
204         lastPos = matcher.end();
205         String type = matcher.group(1);
206         String subtype = matcher.group(2);
207
208         if(!type.equalsIgnoreCase("text")) return null;
209         if(!subtype.equalsIgnoreCase("html")) return null;
210
211         matcher.usePattern(ATTR_PATTERN);
212
213         String charset = null;
214         for(;;){
215             matchResult = matcher.find(lastPos);
216             if(!matchResult) break;
217             lastPos = matcher.end();
218             String attribute = matcher.group(1);
219             String value = matcher.group(2);
220             if(attribute.equalsIgnoreCase("charset")) charset = value;
221         }
222         return charset;
223     }
224
225 }