View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  
18  package org.apache.logging.log4j.core.appender;
19  
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  import java.net.HttpURLConnection;
24  import java.net.URL;
25  import java.nio.charset.Charset;
26  import java.util.Objects;
27  
28  import javax.net.ssl.HttpsURLConnection;
29  
30  import org.apache.logging.log4j.core.Layout;
31  import org.apache.logging.log4j.core.LogEvent;
32  import org.apache.logging.log4j.core.LoggerContext;
33  import org.apache.logging.log4j.core.config.Configuration;
34  import org.apache.logging.log4j.core.config.ConfigurationException;
35  import org.apache.logging.log4j.core.config.Property;
36  import org.apache.logging.log4j.core.net.ssl.LaxHostnameVerifier;
37  import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
38  import org.apache.logging.log4j.core.util.IOUtils;
39  
40  public class HttpURLConnectionManager extends HttpManager {
41  
42      private static final Charset CHARSET = Charset.forName("US-ASCII");
43  
44      private final URL url;
45      private final boolean isHttps;
46      private final String method;
47      private final int connectTimeoutMillis;
48      private final int readTimeoutMillis;
49      private final Property[] headers;
50      private final SslConfiguration sslConfiguration;
51      private final boolean verifyHostname;
52  
53      public HttpURLConnectionManager(final Configuration configuration, final LoggerContext loggerContext, final String name,
54                                      final URL url, final String method, final int connectTimeoutMillis,
55                                      final int readTimeoutMillis,
56                                      final Property[] headers,
57                                      final SslConfiguration sslConfiguration,
58                                      final boolean verifyHostname) {
59          super(configuration, loggerContext, name);
60          this.url = url;
61          if (!(url.getProtocol().equalsIgnoreCase("http") || url.getProtocol().equalsIgnoreCase("https"))) {
62              throw new ConfigurationException("URL must have scheme http or https");
63          }
64          this.isHttps = this.url.getProtocol().equalsIgnoreCase("https");
65          this.method = Objects.requireNonNull(method, "method");
66          this.connectTimeoutMillis = connectTimeoutMillis;
67          this.readTimeoutMillis = readTimeoutMillis;
68          this.headers = headers != null ? headers : new Property[0];
69          this.sslConfiguration = sslConfiguration;
70          if (this.sslConfiguration != null && !isHttps) {
71              throw new ConfigurationException("SSL configuration can only be specified with URL scheme https");
72          }
73          this.verifyHostname = verifyHostname;
74      }
75  
76      @Override
77      public void send(final Layout<?> layout, final LogEvent event) throws IOException {
78          final HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
79          urlConnection.setAllowUserInteraction(false);
80          urlConnection.setDoOutput(true);
81          urlConnection.setDoInput(true);
82          urlConnection.setRequestMethod(method);
83          if (connectTimeoutMillis > 0) {
84              urlConnection.setConnectTimeout(connectTimeoutMillis);
85          }
86          if (readTimeoutMillis > 0) {
87              urlConnection.setReadTimeout(readTimeoutMillis);
88          }
89          if (layout.getContentType() != null) {
90              urlConnection.setRequestProperty("Content-Type", layout.getContentType());
91          }
92          for (final Property header : headers) {
93              urlConnection.setRequestProperty(
94                  header.getName(),
95                  header.isValueNeedsLookup() ? getConfiguration().getStrSubstitutor().replace(event, header.getValue()) : header.getValue());
96          }
97          if (sslConfiguration != null) {
98              ((HttpsURLConnection)urlConnection).setSSLSocketFactory(sslConfiguration.getSslSocketFactory());
99          }
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 }