1 package org.apache.maven.wagon.shared.http;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.util.Objects;
23
24 import org.apache.maven.wagon.ResourceDoesNotExistException;
25 import org.apache.maven.wagon.TransferFailedException;
26 import org.apache.maven.wagon.authorization.AuthorizationException;
27 import org.apache.maven.wagon.proxy.ProxyInfo;
28 import org.codehaus.plexus.util.StringUtils;
29
30 /**
31 * Helper for HTTP related messages.
32 * <p>
33 * <b>Important notice on Reason Phrase</b>:
34 * <ul>
35 * <li>reason phrase was <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html">defined by initial HTTP/1.1
36 * RFC 2616</a>: <cite>The Reason-Phrase is intended to give a short textual description of the Status-Code. The
37 * Status-Code is intended for use by automata and the Reason-Phrase is intended for the human user. The client is not
38 * required to examine or display the Reason- Phrase.</cite></li>
39 * <li>it has been later largely deprecated in <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">the updated
40 * HTTP/1.1 in RFC 7230</a>: <cite>The reason-phrase element exists for the sole purpose of providing a textual
41 * description associated with the numeric status code, mostly out of deference to earlier Internet application
42 * protocols that were more frequently used with interactive text clients. A client SHOULD ignore the reason-phrase
43 * content.</cite></li>
44 * <li>it has been removed from <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.4">HTTP/2 in RFC 7540</a>:
45 * <cite>HTTP/2 does not define a way to carry the version or reason phrase that is included in an HTTP/1.1 status
46 * line.</cite>.</li>
47 * </ul>
48 * The use of Reason Phrase done here to improve the message to the end-user (particularly in case of failures) will
49 * disappear while HTTP/2 is deployed: a new mechanism to provide such a message needs to be defined...
50 *
51 * @since 3.3.4
52 */
53 public class HttpMessageUtils
54 {
55 // status codes here to avoid checkstyle magic number and not have have hard depend on non-wagon classes
56 private static final int SC_UNAUTHORIZED = 401;
57 private static final int SC_FORBIDDEN = 403;
58 private static final int SC_NOT_FOUND = 404;
59 private static final int SC_PROXY_AUTH_REQUIRED = 407;
60 private static final int SC_GONE = 410;
61
62 /**
63 * A HTTP status code used to indicate that the actual response status code is not known at time of message
64 * generation.
65 */
66 public static final int UNKNOWN_STATUS_CODE = -1;
67
68 /**
69 * Format a consistent HTTP transfer debug message combining URL, status code, reason phrase and HTTP
70 * proxy server info.
71 * <p>
72 * URL will always be included in the message. A status code other than {@link #UNKNOWN_STATUS_CODE} will be
73 * included. A reason phrase will only be included if non-empty and status code is not {@link #UNKNOWN_STATUS_CODE}.
74 * Proxy information will only be included if not null.
75 *
76 * @param url the required non-null URL associated with the message
77 * @param statusCode an HTTP response status code
78 * @param reasonPhrase an HTTP reason phrase
79 * @param proxyInfo proxy server used during the transfer, may be null if none used
80 * @return a formatted debug message combining the parameters of this method
81 * @throws NullPointerException if url is null
82 */
83 public static String formatTransferDebugMessage( String url, int statusCode, String reasonPhrase,
84 ProxyInfo proxyInfo )
85 {
86 Objects.requireNonNull( url, "url cannot be null" );
87 String msg = url;
88 if ( statusCode != UNKNOWN_STATUS_CODE )
89 {
90 msg += " -- status code: " + statusCode;
91 if ( StringUtils.isNotEmpty( reasonPhrase ) )
92 {
93 msg += ", reason phrase: " + reasonPhrase;
94 }
95 }
96 if ( proxyInfo != null )
97 {
98 msg += " -- proxy: " + proxyInfo;
99 }
100 return msg;
101 }
102
103 /**
104 * Format a consistent message for HTTP related {@link TransferFailedException}.
105 * <p>
106 * This variation typically used in cases where there is no HTTP transfer response data to extract status code and
107 * reason phrase from. Equivalent to calling {@link #formatTransferFailedMessage(String, int, String, ProxyInfo)}
108 * with {@link #UNKNOWN_STATUS_CODE} and null reason phrase.
109 *
110 * @param url the URL associated with the message
111 * @param proxyInfo proxy server used during the transfer, may be null if none used
112 * @return a formatted failure message combining the parameters of this method
113 */
114 public static String formatTransferFailedMessage( String url, ProxyInfo proxyInfo )
115 {
116 return formatTransferFailedMessage( url, UNKNOWN_STATUS_CODE, null,
117 proxyInfo );
118 }
119
120 /**
121 * Format a consistent message for HTTP related {@link TransferFailedException}.
122 *
123 * @param url the URL associated with the message
124 * @param statusCode an HTTP response status code or {@link #UNKNOWN_STATUS_CODE}
125 * @param reasonPhrase an HTTP status line reason phrase or null if the reason phrase unknown
126 * @param proxyInfo proxy server used during the transfer, may be null if none used
127 * @return a formatted failure message combining the parameters of this method
128 */
129 public static String formatTransferFailedMessage( String url, int statusCode, String reasonPhrase,
130 ProxyInfo proxyInfo )
131 {
132 return formatMessage( "transfer failed for ", url, statusCode, reasonPhrase, proxyInfo );
133 }
134
135 /**
136 * Format a consistent message for HTTP related {@link AuthorizationException}.
137 * <p>
138 * The message will always include the URL and status code provided. If empty, the reason phrase is substituted with
139 * a common reason based on status code. {@link ProxyInfo} is only included in the message if not null.
140 *
141 * @param url the URL associated with the message
142 * @param statusCode an HTTP response status code related to auth
143 * @param reasonPhrase an HTTP status line reason phrase
144 * @param proxyInfo proxy server used during the transfer, may be null if none used
145 * @return a consistent message for a HTTP related {@link AuthorizationException}
146 */
147 // TODO Split when WAGON-568 is implemented
148 public static String formatAuthorizationMessage( String url, int statusCode, String reasonPhrase,
149 ProxyInfo proxyInfo )
150 {
151 switch ( statusCode )
152 {
153 case SC_UNAUTHORIZED: // no credentials or auth was not valid
154 return formatMessage( "authentication failed for ", url, statusCode, reasonPhrase, null );
155
156 case SC_FORBIDDEN: // forbidden based on permissions usually
157 return formatMessage( "authorization failed for ", url, statusCode, reasonPhrase, null );
158
159 case SC_PROXY_AUTH_REQUIRED:
160 return formatMessage( "proxy authentication failed for ", url, statusCode,
161 reasonPhrase, null );
162
163 default:
164 break;
165 }
166
167 return formatMessage( "authorization failed for ", url, statusCode, reasonPhrase, proxyInfo );
168 }
169
170 /**
171 * Format a consistent message for HTTP related {@link ResourceDoesNotExistException}.
172 * <p>
173 * The message will always include the URL and status code provided. If empty, the reason phrase is substituted with
174 * the commonly used reason phrases per status code. {@link ProxyInfo} is only included if not null.
175 *
176 * @param url the URL associated with the message
177 * @param statusCode an HTTP response status code related to resources not being found
178 * @param reasonPhrase an HTTP status line reason phrase
179 * @param proxyInfo proxy server used during the transfer, may be null if none used
180 * @return a consistent message for a HTTP related {@link ResourceDoesNotExistException}
181 */
182 public static String formatResourceDoesNotExistMessage( String url, int statusCode, String reasonPhrase,
183 ProxyInfo proxyInfo )
184 {
185 return formatMessage( "resource missing at ", url, statusCode, reasonPhrase, proxyInfo );
186 }
187
188 private static String formatMessage( String message, String url, int statusCode, String reasonPhrase,
189 ProxyInfo proxyInfo )
190 {
191 Objects.requireNonNull( message, "message cannot be null" );
192 Objects.requireNonNull( url, "url cannot be null" );
193 String msg = message + url;
194 if ( statusCode != UNKNOWN_STATUS_CODE )
195 {
196 msg += ", status: " + statusCode;
197
198 if ( StringUtils.isNotEmpty( reasonPhrase ) )
199 {
200 msg += " " + reasonPhrase;
201 }
202 else
203 {
204 switch ( statusCode )
205 {
206 case SC_UNAUTHORIZED:
207 msg += " Unauthorized";
208 break;
209
210 case SC_FORBIDDEN:
211 msg += " Forbidden";
212 break;
213
214 case SC_NOT_FOUND:
215 msg += " Not Found";
216 break;
217
218 case SC_PROXY_AUTH_REQUIRED:
219 msg += " Proxy Authentication Required";
220 break;
221
222 case SC_GONE:
223 msg += " Gone";
224 break;
225
226 default:
227 break;
228 }
229 }
230 }
231 if ( proxyInfo != null )
232 {
233 msg += ", proxy: " + proxyInfo;
234 }
235 return msg;
236 }
237 }