1 /* 2 * ==================================================================== 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * ==================================================================== 20 * 21 * This software consists of voluntary contributions made by many 22 * individuals on behalf of the Apache Software Foundation. For more 23 * information on the Apache Software Foundation, please see 24 * <http://www.apache.org/>. 25 * 26 */ 27 package org.apache.hc.core5.http.protocol; 28 29 import java.io.IOException; 30 31 import org.apache.hc.core5.annotation.Contract; 32 import org.apache.hc.core5.annotation.ThreadingBehavior; 33 import org.apache.hc.core5.http.EntityDetails; 34 import org.apache.hc.core5.http.HttpHeaders; 35 import org.apache.hc.core5.http.HttpRequest; 36 import org.apache.hc.core5.http.HttpRequestInterceptor; 37 import org.apache.hc.core5.http.HttpVersion; 38 import org.apache.hc.core5.http.ProtocolException; 39 import org.apache.hc.core5.http.ProtocolVersion; 40 import org.apache.hc.core5.net.URIAuthority; 41 import org.apache.hc.core5.util.Args; 42 43 44 /** 45 * This request interceptor can be used by an HTTP proxy or intemediary to add the 46 * {@link HttpHeaders#VIA} HTTP header to outgoing request messages. 47 * <p>The {@link HttpHeaders#VIA} header is used to indicate intermediate protocols and recipients 48 * between the user agent and the server (on requests) or between the origin server and the client 49 * (on responses). It can be used for tracking message forwards, avoiding request loops, and 50 * identifying the protocol capabilities of senders along the request/response chain. Each member of 51 * the {@link HttpHeaders#VIA} header field value represents a proxy or gateway that has forwarded 52 * the message. 53 * <p>A proxy <b>MUST</b> send an appropriate {@link HttpHeaders#VIA} header field, as described 54 * in 55 * the HTTP specification, in each message that it forwards. An HTTP-to-HTTP gateway <b>MUST</b> 56 * send an appropriate {@link HttpHeaders#VIA} header field in each inbound request message and 57 * <b>MAY</b> send a {@link HttpHeaders#VIA} header field in forwarded response messages. 58 * <p>This interceptor ensures that the {@link HttpHeaders#VIA} header is added to the request 59 * only 60 * if it has not been added previously, as per the HTTP specification. Additionally, it updates the 61 * values in the {@link HttpHeaders#VIA} header correctly in case of multiple intermediate protocols 62 * or recipients, by appending its own information about how the message was received to the end of 63 * the header field value. 64 * 65 * @since 5.3 66 */ 67 @Contract(threading = ThreadingBehavior.IMMUTABLE) 68 public class ViaRequest implements HttpRequestInterceptor { 69 70 71 /** 72 * Singleton instance. 73 */ 74 public static final HttpRequestInterceptor INSTANCE = new ViaRequest(); 75 76 /** 77 * Constructs a new {@code ViaRequest}. 78 */ 79 public ViaRequest() { 80 } 81 82 /** 83 * Adds the HTTP {@link HttpHeaders#VIA} header to the request if it does not already exist. 84 * 85 * <p>This method ensures that the version of the request is HTTP/1.1 or higher, and adds the 86 * Via header in the format {@code <protocol> <version> <host>}, where {@code <protocol>} is the protocol name, 87 * {@code <version>} is the major and minor version of the request, and {@code <host>} is the value of the Host header. 88 * 89 * <p>In case the {@link HttpHeaders#VIA} header already exists, this method updates its value by appending 90 * the new protocol information in the same format. 91 * 92 * <p>If the version of the request is lower than {@code HTTP/1.1} or the request authority not being specified, 93 * this method throws a {@link ProtocolException}. 94 * 95 * @param request the request object to modify 96 * @param entity the entity for the request, may be {@code null} 97 * @param context the context for the request 98 * @throws ProtocolException if there was a protocol error, such as the request version being lower than {@code HTTP/1.1}, 99 * or the request authority not being specified 100 * @throws IOException if there was an I/O error 101 */ 102 @Override 103 public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws ProtocolException, IOException { 104 Args.notNull(request, "HTTP request"); 105 Args.notNull(context, "HTTP context"); 106 final ProtocolVersion ver = context.getProtocolVersion() != null ? context.getProtocolVersion() : HttpVersion.HTTP_1_1; 107 108 final URIAuthority authority = request.getAuthority(); 109 if (authority == null) { 110 throw new ProtocolException("Request authority not specified"); 111 } 112 113 114 if (!ver.greaterEquals(HttpVersion.HTTP_1_1)) { 115 throw new ProtocolException("Invalid protocol version: %s", ver); 116 } 117 if (!request.containsHeader(HttpHeaders.VIA)) { 118 String viaHeaderValue = ver.getProtocol() + " " + ver.getMajor() + "." + ver.getMinor() + " " + authority.getHostName(); 119 final int port = authority.getPort(); 120 if (port != -1) { 121 viaHeaderValue += ":" + port; 122 } 123 request.addHeader(HttpHeaders.VIA, viaHeaderValue); 124 } 125 } 126 } 127