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 }