1 /* 2 * Copyright 2014 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc.okhttp; 18 19 import com.google.common.base.Preconditions; 20 import io.grpc.InternalMetadata; 21 import io.grpc.Metadata; 22 import io.grpc.internal.GrpcUtil; 23 import io.grpc.internal.TransportFrameUtil; 24 import io.grpc.okhttp.internal.framed.Header; 25 import java.util.ArrayList; 26 import java.util.List; 27 import okio.ByteString; 28 29 /** 30 * Constants for request/response headers. 31 */ 32 class Headers { 33 34 public static final Header HTTPS_SCHEME_HEADER = new Header(Header.TARGET_SCHEME, "https"); 35 public static final Header HTTP_SCHEME_HEADER = new Header(Header.TARGET_SCHEME, "http"); 36 public static final Header METHOD_HEADER = new Header(Header.TARGET_METHOD, GrpcUtil.HTTP_METHOD); 37 public static final Header METHOD_GET_HEADER = new Header(Header.TARGET_METHOD, "GET"); 38 public static final Header CONTENT_TYPE_HEADER = 39 new Header(GrpcUtil.CONTENT_TYPE_KEY.name(), GrpcUtil.CONTENT_TYPE_GRPC); 40 public static final Header TE_HEADER = new Header("te", GrpcUtil.TE_TRAILERS); 41 42 /** 43 * Serializes the given headers and creates a list of OkHttp {@link Header}s to be used when 44 * creating a stream. Since this serializes the headers, this method should be called in the 45 * application thread context. 46 */ createRequestHeaders( Metadata headers, String defaultPath, String authority, String userAgent, boolean useGet, boolean usePlaintext)47 public static List<Header> createRequestHeaders( 48 Metadata headers, 49 String defaultPath, 50 String authority, 51 String userAgent, 52 boolean useGet, 53 boolean usePlaintext) { 54 Preconditions.checkNotNull(headers, "headers"); 55 Preconditions.checkNotNull(defaultPath, "defaultPath"); 56 Preconditions.checkNotNull(authority, "authority"); 57 58 stripNonApplicationHeaders(headers); 59 60 // 7 is the number of explicit add calls below. 61 List<Header> okhttpHeaders = new ArrayList<>(7 + InternalMetadata.headerCount(headers)); 62 63 // Set GRPC-specific headers. 64 if (usePlaintext) { 65 okhttpHeaders.add(HTTP_SCHEME_HEADER); 66 } else { 67 okhttpHeaders.add(HTTPS_SCHEME_HEADER); 68 } 69 if (useGet) { 70 okhttpHeaders.add(METHOD_GET_HEADER); 71 } else { 72 okhttpHeaders.add(METHOD_HEADER); 73 } 74 75 okhttpHeaders.add(new Header(Header.TARGET_AUTHORITY, authority)); 76 String path = defaultPath; 77 okhttpHeaders.add(new Header(Header.TARGET_PATH, path)); 78 79 okhttpHeaders.add(new Header(GrpcUtil.USER_AGENT_KEY.name(), userAgent)); 80 81 // All non-pseudo headers must come after pseudo headers. 82 okhttpHeaders.add(CONTENT_TYPE_HEADER); 83 okhttpHeaders.add(TE_HEADER); 84 85 // Now add any application-provided headers. 86 return addMetadata(okhttpHeaders, headers); 87 } 88 89 /** 90 * Serializes the given headers and creates a list of OkHttp {@link Header}s to be used when 91 * starting a response. Since this serializes the headers, this method should be called in the 92 * application thread context. 93 */ createResponseHeaders(Metadata headers)94 public static List<Header> createResponseHeaders(Metadata headers) { 95 stripNonApplicationHeaders(headers); 96 97 // 2 is the number of explicit add calls below. 98 List<Header> okhttpHeaders = new ArrayList<>(2 + InternalMetadata.headerCount(headers)); 99 okhttpHeaders.add(new Header(Header.RESPONSE_STATUS, "200")); 100 // All non-pseudo headers must come after pseudo headers. 101 okhttpHeaders.add(CONTENT_TYPE_HEADER); 102 return addMetadata(okhttpHeaders, headers); 103 } 104 105 /** 106 * Serializes the given headers and creates a list of OkHttp {@link Header}s to be used when 107 * finishing a response. Since this serializes the headers, this method should be called in the 108 * application thread context. 109 */ createResponseTrailers(Metadata trailers, boolean headersSent)110 public static List<Header> createResponseTrailers(Metadata trailers, boolean headersSent) { 111 if (!headersSent) { 112 return createResponseHeaders(trailers); 113 } 114 stripNonApplicationHeaders(trailers); 115 116 List<Header> okhttpTrailers = new ArrayList<>(InternalMetadata.headerCount(trailers)); 117 return addMetadata(okhttpTrailers, trailers); 118 } 119 120 /** 121 * Serializes the given headers and creates a list of OkHttp {@link Header}s to be used when 122 * failing with an HTTP response. 123 */ createHttpResponseHeaders( int httpCode, String contentType, Metadata headers)124 public static List<Header> createHttpResponseHeaders( 125 int httpCode, String contentType, Metadata headers) { 126 // 2 is the number of explicit add calls below. 127 List<Header> okhttpHeaders = new ArrayList<>(2 + InternalMetadata.headerCount(headers)); 128 okhttpHeaders.add(new Header(Header.RESPONSE_STATUS, "" + httpCode)); 129 // All non-pseudo headers must come after pseudo headers. 130 okhttpHeaders.add(new Header(GrpcUtil.CONTENT_TYPE_KEY.name(), contentType)); 131 return addMetadata(okhttpHeaders, headers); 132 } 133 addMetadata(List<Header> okhttpHeaders, Metadata toAdd)134 private static List<Header> addMetadata(List<Header> okhttpHeaders, Metadata toAdd) { 135 byte[][] serializedHeaders = TransportFrameUtil.toHttp2Headers(toAdd); 136 for (int i = 0; i < serializedHeaders.length; i += 2) { 137 ByteString key = ByteString.of(serializedHeaders[i]); 138 // Don't allow HTTP/2 pseudo headers to be added by the application. 139 if (key.size() == 0 || key.getByte(0) == ':') { 140 continue; 141 } 142 ByteString value = ByteString.of(serializedHeaders[i + 1]); 143 okhttpHeaders.add(new Header(key, value)); 144 } 145 return okhttpHeaders; 146 } 147 148 /** Strips all non-pseudo headers reserved by gRPC, to avoid duplicates and misinterpretation. */ stripNonApplicationHeaders(Metadata headers)149 private static void stripNonApplicationHeaders(Metadata headers) { 150 headers.discardAll(GrpcUtil.CONTENT_TYPE_KEY); 151 headers.discardAll(GrpcUtil.TE_HEADER); 152 headers.discardAll(GrpcUtil.USER_AGENT_KEY); 153 } 154 } 155