1 /*
   2  * Copyright (c) 2015, 2017, 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.incubator.http.internal.common;
  27 
  28 import jdk.incubator.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.incubator.http.internal.frame.DataFrame;
  37 import jdk.incubator.http.internal.frame.Http2Frame;
  38 import jdk.incubator.http.internal.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
  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     static int logging;
  69 
  70     // Frame types: "control", "data", "window", "all"
  71     public static final int CONTROL = 1; // all except DATA and WINDOW_UPDATES
  72     public static final int DATA = 2;
  73     public static final int WINDOW_UPDATES = 4;
  74     public static final int ALL = CONTROL| DATA | WINDOW_UPDATES;
  75     static int frametypes;
  76 
  77     static final System.Logger logger;
  78 
  79     static {
  80         String s = Utils.getNetProperty(logProp);
  81         if (s == null) {
  82             logging = OFF;
  83         } else {
  84             String[] vals = s.split(",");
  85             for (String val : vals) {
  86                 switch (val.toLowerCase(Locale.US)) {
  87                     case "errors":
  88                         logging |= ERRORS;
  89                         break;
  90                     case "requests":
  91                         logging |= REQUESTS;
  92                         break;
  93                     case "headers":
  94                         logging |= HEADERS;
  95                         break;
  96                     case "content":
  97                         logging |= CONTENT;
  98                         break;
  99                     case "ssl":
 100                         logging |= SSL;
 101                         break;
 102                     case "trace":
 103                         logging |= TRACE;
 104                         break;
 105                     case "all":
 106                         logging |= CONTENT|HEADERS|REQUESTS|FRAMES|ERRORS|TRACE|SSL;
 107                         break;
 108                 }
 109                 if (val.startsWith("frames")) {
 110                     logging |= FRAMES;
 111                     String[] types = val.split(":");
 112                     if (types.length == 1) {
 113                         frametypes = CONTROL | DATA | WINDOW_UPDATES;
 114                     } else {
 115                         for (String type : types) {
 116                             switch (type.toLowerCase()) {
 117                                 case "control":
 118                                     frametypes |= CONTROL;
 119                                     break;
 120                                 case "data":
 121                                     frametypes |= DATA;
 122                                     break;
 123                                 case "window":
 124                                     frametypes |= WINDOW_UPDATES;
 125                                     break;
 126                                 case "all":
 127                                     frametypes = ALL;
 128                                     break;
 129                             }
 130                         }
 131                     }
 132                 }
 133             }
 134         }
 135         if (logging != OFF) {
 136             logger = System.getLogger("jdk.httpclient.HttpClient");
 137         } else {
 138             logger = null;
 139         }
 140     }
 141     public static boolean errors() {
 142         return (logging & ERRORS) != 0;
 143     }
 144 
 145     public static boolean requests() {
 146         return (logging & REQUESTS) != 0;
 147     }
 148 
 149     public static boolean headers() {
 150         return (logging & HEADERS) != 0;
 151     }
 152 
 153     public static boolean trace() {
 154         return (logging & TRACE) != 0;
 155     }
 156 
 157     public static boolean ssl() {
 158         return (logging & SSL) != 0;
 159     }
 160 
 161     public static boolean frames() {
 162         return (logging & FRAMES) != 0;
 163     }
 164 
 165     public static void logError(String s, Object... s1) {
 166         if (errors()) {
 167             logger.log(Level.INFO, "ERROR: " + s, s1);
 168         }
 169     }
 170 
 171     public static void logError(Throwable t) {
 172         if (errors()) {
 173             String s = Utils.stackTrace(t);
 174             logger.log(Level.INFO, "ERROR: " + s);
 175         }
 176     }
 177 
 178     public static void logSSL(String s, Object... s1) {
 179         if (ssl()) {
 180             logger.log(Level.INFO, "SSL: " + s, s1);
 181         }
 182     }
 183 
 184     public static void logSSL(Supplier<String> msgSupplier) {
 185         if (ssl()) {
 186             logger.log(Level.INFO, "SSL: " + msgSupplier.get());
 187         }
 188     }
 189 
 190     public static void logTrace(String s, Object... s1) {
 191         if (trace()) {
 192             String format = "TRACE: " + s;
 193             logger.log(Level.INFO, format, s1);
 194         }
 195     }
 196 
 197     public static void logRequest(String s, Object... s1) {
 198         if (requests()) {
 199             logger.log(Level.INFO, "REQUEST: " + s, s1);
 200         }
 201     }
 202 
 203     public static void logResponse(Supplier<String> supplier) {
 204         if (requests()) {
 205             logger.log(Level.INFO, "RESPONSE: " + supplier.get());
 206         }
 207     }
 208 
 209     public static void logHeaders(String s, Object... s1) {
 210         if (headers()) {
 211             logger.log(Level.INFO, "HEADERS: " + s, s1);
 212         }
 213     }
 214 
 215     public static boolean loggingFrame(Class<? extends Http2Frame> clazz) {
 216         if (frametypes == ALL) {
 217             return true;
 218         }
 219         if (clazz == DataFrame.class) {
 220             return (frametypes & DATA) != 0;
 221         } else if (clazz == WindowUpdateFrame.class) {
 222             return (frametypes & WINDOW_UPDATES) != 0;
 223         } else {
 224             return (frametypes & CONTROL) != 0;
 225         }
 226     }
 227 
 228     public static void logFrames(Http2Frame f, String direction) {
 229         if (frames() && loggingFrame(f.getClass())) {
 230             logger.log(Level.INFO, "FRAME: " + direction + ": " + f.toString());
 231         }
 232     }
 233 
 234     public static void logParams(SSLParameters p) {
 235         if (!Log.ssl()) {
 236             return;
 237         }
 238 
 239         if (p == null) {
 240             Log.logSSL("SSLParameters: Null params");
 241             return;
 242         }
 243 
 244         final StringBuilder sb = new StringBuilder("SSLParameters:");
 245         final List<Object> params = new ArrayList<>();
 246         if (p.getCipherSuites() != null) {
 247             for (String cipher : p.getCipherSuites()) {
 248                 sb.append("\n    cipher: {")
 249                         .append(params.size()).append("}");
 250                 params.add(cipher);
 251             }
 252         }
 253 
 254         // SSLParameters.getApplicationProtocols() can't return null
 255         // JDK 8 EXCL START
 256         for (String approto : p.getApplicationProtocols()) {
 257             sb.append("\n    application protocol: {")
 258                     .append(params.size()).append("}");
 259             params.add(approto);
 260         }
 261         // JDK 8 EXCL END
 262 
 263         if (p.getProtocols() != null) {
 264             for (String protocol : p.getProtocols()) {
 265                 sb.append("\n    protocol: {")
 266                         .append(params.size()).append("}");
 267                 params.add(protocol);
 268             }
 269         }
 270 
 271         if (p.getServerNames() != null) {
 272             for (SNIServerName sname : p.getServerNames()) {
 273                 sb.append("\n    server name: {")
 274                         .append(params.size()).append("}");
 275                 params.add(sname.toString());
 276             }
 277         }
 278         sb.append('\n');
 279 
 280         Log.logSSL(sb.toString(), params.toArray());
 281     }
 282 
 283     public static void dumpHeaders(StringBuilder sb, String prefix, HttpHeaders headers) {
 284         if (headers != null) {
 285             Map<String,List<String>> h = headers.map();
 286             Set<Map.Entry<String,List<String>>> entries = h.entrySet();
 287             for (Map.Entry<String,List<String>> entry : entries) {
 288                 String key = entry.getKey();
 289                 List<String> values = entry.getValue();
 290                 sb.append(prefix).append(key).append(":");
 291                 for (String value : values) {
 292                     sb.append(' ').append(value);
 293                 }
 294                 sb.append('\n');
 295             }
 296         }
 297     }
 298 
 299 
 300     // not instantiable
 301     private Log() {}
 302 }