1 /*
   2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.internal.net.http.common;
  27 
  28 import java.net.http.HttpHeaders;
  29 
  30 import java.util.ArrayList;
  31 import java.util.List;
  32 import java.util.Locale;
  33 import java.util.Map;
  34 import java.util.Set;
  35 import java.util.function.Supplier;
  36 import jdk.internal.net.http.frame.DataFrame;
  37 import jdk.internal.net.http.frame.Http2Frame;
  38 import jdk.internal.net.http.frame.WindowUpdateFrame;
  39 
  40 import javax.net.ssl.SNIServerName;
  41 import javax.net.ssl.SSLParameters;
  42 
  43 /**
  44  * -Djava.net.HttpClient.log=
  45  *          errors,requests,headers,
  46  *          frames[:control:data:window:all..],content,ssl,trace,channel
  47  *
  48  * Any of errors, requests, headers or content are optional.
  49  *
  50  * Other handlers may be added. All logging is at level INFO
  51  *
  52  * Logger name is "jdk.httpclient.HttpClient"
  53  */
  54 // implements System.Logger in order to be skipped when printing the caller's
  55 // information
  56 public abstract class Log implements System.Logger {
  57 
  58     static final String logProp = "jdk.httpclient.HttpClient.log";
  59 
  60     public static final int OFF = 0;
  61     public static final int ERRORS = 0x1;
  62     public static final int REQUESTS = 0x2;
  63     public static final int HEADERS = 0x4;
  64     public static final int CONTENT = 0x8;
  65     public static final int FRAMES = 0x10;
  66     public static final int SSL = 0x20;
  67     public static final int TRACE = 0x40;
  68     public static final int CHANNEL = 0x80;
  69     static int logging;
  70 
  71     // Frame types: "control", "data", "window", "all"
  72     public static final int CONTROL = 1; // all except DATA and WINDOW_UPDATES
  73     public static final int DATA = 2;
  74     public static final int WINDOW_UPDATES = 4;
  75     public static final int ALL = CONTROL| DATA | WINDOW_UPDATES;
  76     static int frametypes;
  77 
  78     static final System.Logger logger;
  79 
  80     static {
  81         String s = Utils.getNetProperty(logProp);
  82         if (s == null) {
  83             logging = OFF;
  84         } else {
  85             String[] vals = s.split(",");
  86             for (String val : vals) {
  87                 switch (val.toLowerCase(Locale.US)) {
  88                     case "errors":
  89                         logging |= ERRORS;
  90                         break;
  91                     case "requests":
  92                         logging |= REQUESTS;
  93                         break;
  94                     case "headers":
  95                         logging |= HEADERS;
  96                         break;
  97                     case "content":
  98                         logging |= CONTENT;
  99                         break;
 100                     case "ssl":
 101                         logging |= SSL;
 102                         break;
 103                     case "channel":
 104                         logging |= CHANNEL;
 105                         break;
 106                     case "trace":
 107                         logging |= TRACE;
 108                         break;
 109                     case "all":
 110                         logging |= CONTENT|HEADERS|REQUESTS|FRAMES|ERRORS|TRACE|SSL| CHANNEL;
 111                         frametypes |= ALL;
 112                         break;
 113                     default:
 114                         // ignore bad values
 115                 }
 116                 if (val.startsWith("frames")) {
 117                     logging |= FRAMES;
 118                     String[] types = val.split(":");
 119                     if (types.length == 1) {
 120                         frametypes = CONTROL | DATA | WINDOW_UPDATES;
 121                     } else {
 122                         for (String type : types) {
 123                             switch (type.toLowerCase(Locale.US)) {
 124                                 case "control":
 125                                     frametypes |= CONTROL;
 126                                     break;
 127                                 case "data":
 128                                     frametypes |= DATA;
 129                                     break;
 130                                 case "window":
 131                                     frametypes |= WINDOW_UPDATES;
 132                                     break;
 133                                 case "all":
 134                                     frametypes = ALL;
 135                                     break;
 136                                 default:
 137                                     // ignore bad values
 138                             }
 139                         }
 140                     }
 141                 }
 142             }
 143         }
 144         if (logging != OFF) {
 145             logger = System.getLogger("jdk.httpclient.HttpClient");
 146         } else {
 147             logger = null;
 148         }
 149     }
 150     public static boolean errors() {
 151         return (logging & ERRORS) != 0;
 152     }
 153 
 154     public static boolean requests() {
 155         return (logging & REQUESTS) != 0;
 156     }
 157 
 158     public static boolean headers() {
 159         return (logging & HEADERS) != 0;
 160     }
 161 
 162     public static boolean trace() {
 163         return (logging & TRACE) != 0;
 164     }
 165 
 166     public static boolean ssl() {
 167         return (logging & SSL) != 0;
 168     }
 169 
 170     public static boolean frames() {
 171         return (logging & FRAMES) != 0;
 172     }
 173 
 174     public static boolean channel() {
 175         return (logging & CHANNEL) != 0;
 176     }
 177 
 178     public static void logError(String s, Object... s1) {
 179         if (errors()) {
 180             logger.log(Level.INFO, "ERROR: " + s, s1);
 181         }
 182     }
 183 
 184     public static void logError(Throwable t) {
 185         if (errors()) {
 186             String s = Utils.stackTrace(t);
 187             logger.log(Level.INFO, "ERROR: " + s);
 188         }
 189     }
 190 
 191     public static void logSSL(String s, Object... s1) {
 192         if (ssl()) {
 193             logger.log(Level.INFO, "SSL: " + s, s1);
 194         }
 195     }
 196 
 197     public static void logSSL(Supplier<String> msgSupplier) {
 198         if (ssl()) {
 199             logger.log(Level.INFO, "SSL: " + msgSupplier.get());
 200         }
 201     }
 202 
 203     public static void logChannel(String s, Object... s1) {
 204         if (channel()) {
 205             logger.log(Level.INFO, "CHANNEL: " + s, s1);
 206         }
 207     }
 208 
 209     public static void logChannel(Supplier<String> msgSupplier) {
 210         if (channel()) {
 211             logger.log(Level.INFO, "CHANNEL: " + msgSupplier.get());
 212         }
 213     }
 214 
 215     public static void logTrace(String s, Object... s1) {
 216         if (trace()) {
 217             String format = "MISC: " + s;
 218             logger.log(Level.INFO, format, s1);
 219         }
 220     }
 221 
 222     public static void logRequest(String s, Object... s1) {
 223         if (requests()) {
 224             logger.log(Level.INFO, "REQUEST: " + s, s1);
 225         }
 226     }
 227 
 228     public static void logResponse(Supplier<String> supplier) {
 229         if (requests()) {
 230             logger.log(Level.INFO, "RESPONSE: " + supplier.get());
 231         }
 232     }
 233 
 234     public static void logHeaders(String s, Object... s1) {
 235         if (headers()) {
 236             logger.log(Level.INFO, "HEADERS: " + s, s1);
 237         }
 238     }
 239 
 240     public static boolean loggingFrame(Class<? extends Http2Frame> clazz) {
 241         if (frametypes == ALL) {
 242             return true;
 243         }
 244         if (clazz == DataFrame.class) {
 245             return (frametypes & DATA) != 0;
 246         } else if (clazz == WindowUpdateFrame.class) {
 247             return (frametypes & WINDOW_UPDATES) != 0;
 248         } else {
 249             return (frametypes & CONTROL) != 0;
 250         }
 251     }
 252 
 253     public static void logFrames(Http2Frame f, String direction) {
 254         if (frames() && loggingFrame(f.getClass())) {
 255             logger.log(Level.INFO, "FRAME: " + direction + ": " + f.toString());
 256         }
 257     }
 258 
 259     public static void logParams(SSLParameters p) {
 260         if (!Log.ssl()) {
 261             return;
 262         }
 263 
 264         if (p == null) {
 265             Log.logSSL("SSLParameters: Null params");
 266             return;
 267         }
 268 
 269         final StringBuilder sb = new StringBuilder("SSLParameters:");
 270         final List<Object> params = new ArrayList<>();
 271         if (p.getCipherSuites() != null) {
 272             for (String cipher : p.getCipherSuites()) {
 273                 sb.append("\n    cipher: {")
 274                         .append(params.size()).append("}");
 275                 params.add(cipher);
 276             }
 277         }
 278 
 279         // SSLParameters.getApplicationProtocols() can't return null
 280         // JDK 8 EXCL START
 281         for (String approto : p.getApplicationProtocols()) {
 282             sb.append("\n    application protocol: {")
 283                     .append(params.size()).append("}");
 284             params.add(approto);
 285         }
 286         // JDK 8 EXCL END
 287 
 288         if (p.getProtocols() != null) {
 289             for (String protocol : p.getProtocols()) {
 290                 sb.append("\n    protocol: {")
 291                         .append(params.size()).append("}");
 292                 params.add(protocol);
 293             }
 294         }
 295 
 296         if (p.getEndpointIdentificationAlgorithm() != null) {
 297             sb.append("\n    endpointIdAlg: {")
 298                 .append(params.size()).append("}");
 299             params.add(p.getEndpointIdentificationAlgorithm());
 300         }
 301 
 302         if (p.getServerNames() != null) {
 303             for (SNIServerName sname : p.getServerNames()) {
 304                 sb.append("\n    server name: {")
 305                         .append(params.size()).append("}");
 306                 params.add(sname.toString());
 307             }
 308         }
 309         sb.append('\n');
 310 
 311         Log.logSSL(sb.toString(), params.toArray());
 312     }
 313 
 314     public static void dumpHeaders(StringBuilder sb, String prefix, HttpHeaders headers) {
 315         if (headers != null) {
 316             Map<String,List<String>> h = headers.map();
 317             Set<Map.Entry<String,List<String>>> entries = h.entrySet();
 318             String sep = "";
 319             for (Map.Entry<String,List<String>> entry : entries) {
 320                 String key = entry.getKey();
 321                 List<String> values = entry.getValue();
 322                 if (values == null || values.isEmpty()) {
 323                     // should not happen
 324                     sb.append(sep);
 325                     sb.append(prefix).append(key).append(':');
 326                     sep = "\n";
 327                     continue;
 328                 }
 329                 for (String value : values) {
 330                     sb.append(sep);
 331                     sb.append(prefix).append(key).append(':');
 332                     sb.append(' ').append(value);
 333                     sep = "\n";
 334                 }
 335             }
 336             sb.append('\n');
 337         }
 338     }
 339 
 340 
 341     // not instantiable
 342     private Log() {}
 343 }