001package org.apache.maven.wagon.shared.http; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.util.Objects; 023 024import org.apache.maven.wagon.ResourceDoesNotExistException; 025import org.apache.maven.wagon.TransferFailedException; 026import org.apache.maven.wagon.authorization.AuthorizationException; 027import org.apache.maven.wagon.proxy.ProxyInfo; 028import org.codehaus.plexus.util.StringUtils; 029 030/** 031 * Helper for HTTP related messages. 032 * <p> 033 * <b>Important notice on Reason Phrase</b>: 034 * <ul> 035 * <li>reason phrase was <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html">defined by initial HTTP/1.1 036 * RFC 2616</a>: <cite>The Reason-Phrase is intended to give a short textual description of the Status-Code. The 037 * Status-Code is intended for use by automata and the Reason-Phrase is intended for the human user. The client is not 038 * required to examine or display the Reason- Phrase.</cite></li> 039 * <li>it has been later largely deprecated in <a href="https://tools.ietf.org/html/rfc7230#section-3.1.2">the updated 040 * HTTP/1.1 in RFC 7230</a>: <cite>The reason-phrase element exists for the sole purpose of providing a textual 041 * description associated with the numeric status code, mostly out of deference to earlier Internet application 042 * protocols that were more frequently used with interactive text clients. A client SHOULD ignore the reason-phrase 043 * content.</cite></li> 044 * <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>: 045 * <cite>HTTP/2 does not define a way to carry the version or reason phrase that is included in an HTTP/1.1 status 046 * line.</cite>.</li> 047 * </ul> 048 * The use of Reason Phrase done here to improve the message to the end-user (particularly in case of failures) will 049 * disappear while HTTP/2 is deployed: a new mechanism to provide such a message needs to be defined... 050 * 051 * @since 3.3.4 052 */ 053public class HttpMessageUtils 054{ 055 // status codes here to avoid checkstyle magic number and not have have hard depend on non-wagon classes 056 private static final int SC_UNAUTHORIZED = 401; 057 private static final int SC_FORBIDDEN = 403; 058 private static final int SC_NOT_FOUND = 404; 059 private static final int SC_PROXY_AUTH_REQUIRED = 407; 060 private static final int SC_GONE = 410; 061 062 /** 063 * A HTTP status code used to indicate that the actual response status code is not known at time of message 064 * generation. 065 */ 066 public static final int UNKNOWN_STATUS_CODE = -1; 067 068 /** 069 * Format a consistent HTTP transfer debug message combining URL, status code, reason phrase and HTTP 070 * proxy server info. 071 * <p> 072 * URL will always be included in the message. A status code other than {@link #UNKNOWN_STATUS_CODE} will be 073 * included. A reason phrase will only be included if non-empty and status code is not {@link #UNKNOWN_STATUS_CODE}. 074 * Proxy information will only be included if not null. 075 * 076 * @param url the required non-null URL associated with the message 077 * @param statusCode an HTTP response status code 078 * @param reasonPhrase an HTTP reason phrase 079 * @param proxyInfo proxy server used during the transfer, may be null if none used 080 * @return a formatted debug message combining the parameters of this method 081 * @throws NullPointerException if url is null 082 */ 083 public static String formatTransferDebugMessage( String url, int statusCode, String reasonPhrase, 084 ProxyInfo proxyInfo ) 085 { 086 Objects.requireNonNull( url, "url cannot be null" ); 087 String msg = url; 088 if ( statusCode != UNKNOWN_STATUS_CODE ) 089 { 090 msg += " -- status code: " + statusCode; 091 if ( StringUtils.isNotEmpty( reasonPhrase ) ) 092 { 093 msg += ", reason phrase: " + reasonPhrase; 094 } 095 } 096 if ( proxyInfo != null ) 097 { 098 msg += " -- proxy: " + proxyInfo; 099 } 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}