001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 018package org.apache.logging.log4j.core.appender; 019 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.OutputStream; 023import java.net.HttpURLConnection; 024import java.net.URL; 025import java.nio.charset.Charset; 026import java.util.Objects; 027 028import javax.net.ssl.HttpsURLConnection; 029 030import org.apache.logging.log4j.core.Layout; 031import org.apache.logging.log4j.core.LogEvent; 032import org.apache.logging.log4j.core.LoggerContext; 033import org.apache.logging.log4j.core.config.Configuration; 034import org.apache.logging.log4j.core.config.ConfigurationException; 035import org.apache.logging.log4j.core.config.Property; 036import org.apache.logging.log4j.core.net.ssl.LaxHostnameVerifier; 037import org.apache.logging.log4j.core.net.ssl.SslConfiguration; 038import org.apache.logging.log4j.core.util.IOUtils; 039 040public class HttpURLConnectionManager extends HttpManager { 041 042 private static final Charset CHARSET = Charset.forName("US-ASCII"); 043 044 private final URL url; 045 private final boolean isHttps; 046 private final String method; 047 private final int connectTimeoutMillis; 048 private final int readTimeoutMillis; 049 private final Property[] headers; 050 private final SslConfiguration sslConfiguration; 051 private final boolean verifyHostname; 052 053 public HttpURLConnectionManager(final Configuration configuration, final LoggerContext loggerContext, final String name, 054 final URL url, final String method, final int connectTimeoutMillis, 055 final int readTimeoutMillis, 056 final Property[] headers, 057 final SslConfiguration sslConfiguration, 058 final boolean verifyHostname) { 059 super(configuration, loggerContext, name); 060 this.url = url; 061 if (!(url.getProtocol().equalsIgnoreCase("http") || url.getProtocol().equalsIgnoreCase("https"))) { 062 throw new ConfigurationException("URL must have scheme http or https"); 063 } 064 this.isHttps = this.url.getProtocol().equalsIgnoreCase("https"); 065 this.method = Objects.requireNonNull(method, "method"); 066 this.connectTimeoutMillis = connectTimeoutMillis; 067 this.readTimeoutMillis = readTimeoutMillis; 068 this.headers = headers != null ? headers : Property.EMPTY_ARRAY; 069 this.sslConfiguration = sslConfiguration; 070 if (this.sslConfiguration != null && !isHttps) { 071 throw new ConfigurationException("SSL configuration can only be specified with URL scheme https"); 072 } 073 this.verifyHostname = verifyHostname; 074 } 075 076 @Override 077 public void send(final Layout<?> layout, final LogEvent event) throws IOException { 078 final HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection(); 079 urlConnection.setAllowUserInteraction(false); 080 urlConnection.setDoOutput(true); 081 urlConnection.setDoInput(true); 082 urlConnection.setRequestMethod(method); 083 if (connectTimeoutMillis > 0) { 084 urlConnection.setConnectTimeout(connectTimeoutMillis); 085 } 086 if (readTimeoutMillis > 0) { 087 urlConnection.setReadTimeout(readTimeoutMillis); 088 } 089 if (layout.getContentType() != null) { 090 urlConnection.setRequestProperty("Content-Type", layout.getContentType()); 091 } 092 for (final Property header : headers) { 093 urlConnection.setRequestProperty( 094 header.getName(), 095 header.isValueNeedsLookup() ? getConfiguration().getStrSubstitutor().replace(event, header.getValue()) : header.getValue()); 096 } 097 if (sslConfiguration != null) { 098 ((HttpsURLConnection)urlConnection).setSSLSocketFactory(sslConfiguration.getSslSocketFactory()); 099 } 100 if (isHttps && !verifyHostname) { 101 ((HttpsURLConnection)urlConnection).setHostnameVerifier(LaxHostnameVerifier.INSTANCE); 102 } 103 104 final byte[] msg = layout.toByteArray(event); 105 urlConnection.setFixedLengthStreamingMode(msg.length); 106 urlConnection.connect(); 107 try (OutputStream os = urlConnection.getOutputStream()) { 108 os.write(msg); 109 } 110 111 final byte[] buffer = new byte[1024]; 112 try (InputStream is = urlConnection.getInputStream()) { 113 while (IOUtils.EOF != is.read(buffer)) { 114 // empty 115 } 116 } catch (final IOException e) { 117 final StringBuilder errorMessage = new StringBuilder(); 118 try (InputStream es = urlConnection.getErrorStream()) { 119 errorMessage.append(urlConnection.getResponseCode()); 120 if (urlConnection.getResponseMessage() != null) { 121 errorMessage.append(' ').append(urlConnection.getResponseMessage()); 122 } 123 if (es != null) { 124 errorMessage.append(" - "); 125 int n; 126 while (IOUtils.EOF != (n = es.read(buffer))) { 127 errorMessage.append(new String(buffer, 0, n, CHARSET)); 128 } 129 } 130 } 131 if (urlConnection.getResponseCode() > -1) { 132 throw new IOException(errorMessage.toString()); 133 } else { 134 throw e; 135 } 136 } 137 } 138 139}