Line data Source code
1 : // Copyright (C) 2018 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.git; 16 : 17 : import com.google.gerrit.server.logging.TraceContext; 18 : import java.util.List; 19 : import java.util.Optional; 20 : import org.eclipse.jgit.transport.FetchV2Request; 21 : import org.eclipse.jgit.transport.LsRefsV2Request; 22 : import org.eclipse.jgit.transport.ProtocolV2Hook; 23 : 24 : /** 25 : * Git hook for ls-refs and fetch that enables Gerrit request tracing if the user sets the 'trace' 26 : * server option. 27 : * 28 : * <p>This hook is only invoked if Git protocol v2 is used. 29 : * 30 : * <p>If the 'trace' server option is specified without value, this means without providing a trace 31 : * ID, a trace ID is generated, but it's not returned to the client. Hence users are advised to 32 : * always provide a trace ID. 33 : */ 34 10 : public class TracingHook implements ProtocolV2Hook, AutoCloseable { 35 : private TraceContext traceContext; 36 : 37 : @Override 38 : public void onLsRefs(LsRefsV2Request req) { 39 10 : maybeStartTrace(req.getServerOptions()); 40 10 : } 41 : 42 : @Override 43 : public void onFetch(FetchV2Request req) { 44 10 : maybeStartTrace(req.getServerOptions()); 45 10 : } 46 : 47 : @Override 48 : public void close() { 49 10 : if (traceContext != null) { 50 10 : traceContext.close(); 51 : } 52 10 : } 53 : 54 : /** 55 : * Starts request tracing if 'trace' server option is set. 56 : * 57 : * @param serverOptionList list of provided server options 58 : */ 59 : private void maybeStartTrace(List<String> serverOptionList) { 60 10 : if (traceContext != null) { 61 : // Trace was already started 62 2 : return; 63 : } 64 : 65 10 : Optional<String> traceOption = parseTraceOption(serverOptionList); 66 10 : traceContext = 67 10 : TraceContext.newTrace( 68 10 : traceOption.isPresent(), 69 10 : traceOption.orElse(null), 70 : (tagName, traceId) -> { 71 : // TODO(ekempin): Return trace ID to client 72 1 : }); 73 10 : } 74 : 75 : private Optional<String> parseTraceOption(List<String> serverOptionList) { 76 10 : if (serverOptionList == null || serverOptionList.isEmpty()) { 77 10 : return Optional.empty(); 78 : } 79 : 80 1 : Optional<String> traceOption = 81 1 : serverOptionList.stream().filter(o -> o.startsWith("trace")).findAny(); 82 1 : if (!traceOption.isPresent()) { 83 0 : return Optional.empty(); 84 : } 85 : 86 1 : int e = traceOption.get().indexOf('='); 87 1 : if (e > 0) { 88 : // trace option was specified with trace ID: "--trace=<trace-ID>" 89 1 : return Optional.of(traceOption.get().substring(e + 1)); 90 : } 91 : 92 : // trace option was specified without trace ID: "--trace", 93 : // return an empty string so that a trace ID is generated 94 0 : return Optional.of(""); 95 : } 96 : }