wa-lang.org/wazero@v1.0.2/imports/proxywasm/_proxytest/http.go (about) 1 // Copyright 2020-2021 Tetrate 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 proxytest 16 17 import ( 18 "log" 19 "strings" 20 21 "wa-lang.org/wazero/imports/proxywasm/internal" 22 "wa-lang.org/wazero/imports/proxywasm/types" 23 ) 24 25 type ( 26 httpHostEmulator struct { 27 httpStreams map[uint32]*httpStreamState 28 } 29 httpStreamState struct { 30 requestHeaders, responseHeaders [][2]string 31 requestTrailers, responseTrailers [][2]string 32 33 // bodyBuffer keeps the body read so far when plugins request buffering 34 // by returning types.ActionPause. Buffers are cleared when types.ActionContinue 35 // is returned. 36 requestBodyBuffer, responseBodyBuffer []byte 37 // body is the body visible to the plugin, which will include anything 38 // in its bodyBuffer as well. If types.ActionContinue is returned, the 39 // content of body is sent to the upstream or downstream. 40 requestBody, responseBody []byte 41 42 action types.Action 43 sentLocalResponse *LocalHttpResponse 44 } 45 LocalHttpResponse struct { 46 StatusCode uint32 47 StatusCodeDetail string 48 Data []byte 49 Headers [][2]string 50 GRPCStatus int32 51 } 52 ) 53 54 func newHttpHostEmulator() *httpHostEmulator { 55 host := &httpHostEmulator{httpStreams: map[uint32]*httpStreamState{}} 56 return host 57 } 58 59 // impl internal.ProxyWasmHost: delegated from hostEmulator 60 func (h *httpHostEmulator) httpHostEmulatorProxyGetBufferBytes(bt internal.BufferType, start int, maxSize int, 61 returnBufferData **byte, returnBufferSize *int) internal.Status { 62 active := internal.VMStateGetActiveContextID() 63 stream := h.httpStreams[active] 64 var buf []byte 65 switch bt { 66 case internal.BufferTypeHttpRequestBody: 67 buf = stream.requestBody 68 case internal.BufferTypeHttpResponseBody: 69 buf = stream.responseBody 70 default: 71 panic("unreachable: maybe a bug in this host emulation or SDK") 72 } 73 74 if len(buf) == 0 { 75 return internal.StatusNotFound 76 } else if start >= len(buf) { 77 log.Printf("start index out of range: %d (start) >= %d ", start, len(buf)) 78 return internal.StatusBadArgument 79 } 80 81 *returnBufferData = &buf[start] 82 if maxSize > len(buf)-start { 83 *returnBufferSize = len(buf) - start 84 } else { 85 *returnBufferSize = maxSize 86 } 87 return internal.StatusOK 88 } 89 90 func (h *httpHostEmulator) httpHostEmulatorProxySetBufferBytes(bt internal.BufferType, start int, maxSize int, 91 bufferData *byte, bufferSize int) internal.Status { 92 active := internal.VMStateGetActiveContextID() 93 stream := h.httpStreams[active] 94 var targetBuf *[]byte 95 switch bt { 96 case internal.BufferTypeHttpRequestBody: 97 targetBuf = &stream.requestBody 98 case internal.BufferTypeHttpResponseBody: 99 targetBuf = &stream.responseBody 100 default: 101 panic("unreachable: maybe a bug in this host emulation or SDK") 102 } 103 104 body := internal.RawBytePtrToByteSlice(bufferData, bufferSize) 105 if start == 0 { 106 if maxSize == 0 { 107 // Prepend 108 *targetBuf = append(body, *targetBuf...) 109 return internal.StatusOK 110 } else if maxSize >= len(*targetBuf) { 111 // Replace 112 *targetBuf = body 113 return internal.StatusOK 114 } else { 115 return internal.StatusBadArgument 116 } 117 } else if start >= len(*targetBuf) { 118 // Append. 119 *targetBuf = append(*targetBuf, body...) 120 return internal.StatusOK 121 } else { 122 return internal.StatusBadArgument 123 } 124 } 125 126 // impl internal.ProxyWasmHost: delegated from hostEmulator 127 func (h *httpHostEmulator) httpHostEmulatorProxyGetHeaderMapValue(mapType internal.MapType, keyData *byte, 128 keySize int, returnValueData **byte, returnValueSize *int) internal.Status { 129 active := internal.VMStateGetActiveContextID() 130 stream := h.httpStreams[active] 131 132 var headers [][2]string 133 switch mapType { 134 case internal.MapTypeHttpRequestHeaders: 135 headers = stream.requestHeaders 136 case internal.MapTypeHttpResponseHeaders: 137 headers = stream.responseHeaders 138 case internal.MapTypeHttpRequestTrailers: 139 headers = stream.requestTrailers 140 case internal.MapTypeHttpResponseTrailers: 141 headers = stream.responseTrailers 142 default: 143 panic("unreachable: maybe a bug in this host emulation or SDK") 144 } 145 146 key := strings.ToLower(internal.RawBytePtrToString(keyData, keySize)) 147 148 for _, h := range headers { 149 if h[0] == key { 150 // Leading LWS doesn't affect header values, 151 // and often ignored in HTTP parsers. 152 value := []byte(strings.TrimSpace(h[1])) 153 // If the value is empty, 154 // Envoy ignores such headers and return NotFound. 155 if len(value) == 0 { 156 return internal.StatusNotFound 157 } 158 *returnValueData = &value[0] 159 *returnValueSize = len(value) 160 return internal.StatusOK 161 } 162 } 163 164 return internal.StatusNotFound 165 } 166 167 // impl internal.ProxyWasmHost 168 func (h *httpHostEmulator) ProxyAddHeaderMapValue(mapType internal.MapType, keyData *byte, 169 keySize int, valueData *byte, valueSize int) internal.Status { 170 171 key := internal.RawBytePtrToString(keyData, keySize) 172 value := internal.RawBytePtrToString(valueData, valueSize) 173 active := internal.VMStateGetActiveContextID() 174 stream := h.httpStreams[active] 175 176 switch mapType { 177 case internal.MapTypeHttpRequestHeaders: 178 stream.requestHeaders = addMapValue(stream.requestHeaders, key, value) 179 case internal.MapTypeHttpResponseHeaders: 180 stream.responseHeaders = addMapValue(stream.responseHeaders, key, value) 181 case internal.MapTypeHttpRequestTrailers: 182 stream.requestTrailers = addMapValue(stream.requestTrailers, key, value) 183 case internal.MapTypeHttpResponseTrailers: 184 stream.responseTrailers = addMapValue(stream.responseTrailers, key, value) 185 default: 186 panic("unimplemented") 187 } 188 189 return internal.StatusOK 190 } 191 192 func addMapValue(base [][2]string, key, value string) [][2]string { 193 key = strings.ToLower(key) 194 for i, h := range base { 195 if h[0] == key { 196 h[1] += value 197 base[i] = h 198 return base 199 } 200 } 201 return append(base, [2]string{key, value}) 202 } 203 204 // impl internal.ProxyWasmHost 205 func (h *httpHostEmulator) ProxyReplaceHeaderMapValue(mapType internal.MapType, keyData *byte, 206 keySize int, valueData *byte, valueSize int) internal.Status { 207 key := internal.RawBytePtrToString(keyData, keySize) 208 value := internal.RawBytePtrToString(valueData, valueSize) 209 active := internal.VMStateGetActiveContextID() 210 stream := h.httpStreams[active] 211 212 switch mapType { 213 case internal.MapTypeHttpRequestHeaders: 214 stream.requestHeaders = replaceMapValue(stream.requestHeaders, key, value) 215 case internal.MapTypeHttpResponseHeaders: 216 stream.responseHeaders = replaceMapValue(stream.responseHeaders, key, value) 217 case internal.MapTypeHttpRequestTrailers: 218 stream.requestTrailers = replaceMapValue(stream.requestTrailers, key, value) 219 case internal.MapTypeHttpResponseTrailers: 220 stream.responseTrailers = replaceMapValue(stream.responseTrailers, key, value) 221 default: 222 panic("unimplemented") 223 } 224 return internal.StatusOK 225 } 226 227 // impl internal.ProxyWasmHost 228 func replaceMapValue(base [][2]string, key, value string) [][2]string { 229 key = strings.ToLower(key) 230 for i, h := range base { 231 if h[0] == key { 232 h[1] = value 233 base[i] = h 234 return base 235 } 236 } 237 return append(base, [2]string{key, value}) 238 } 239 240 // impl internal.ProxyWasmHost 241 func (h *httpHostEmulator) ProxyRemoveHeaderMapValue(mapType internal.MapType, keyData *byte, keySize int) internal.Status { 242 key := internal.RawBytePtrToString(keyData, keySize) 243 active := internal.VMStateGetActiveContextID() 244 stream := h.httpStreams[active] 245 246 switch mapType { 247 case internal.MapTypeHttpRequestHeaders: 248 stream.requestHeaders = removeHeaderMapValue(stream.requestHeaders, key) 249 case internal.MapTypeHttpResponseHeaders: 250 stream.responseHeaders = removeHeaderMapValue(stream.responseHeaders, key) 251 case internal.MapTypeHttpRequestTrailers: 252 stream.requestTrailers = removeHeaderMapValue(stream.requestTrailers, key) 253 case internal.MapTypeHttpResponseTrailers: 254 stream.responseTrailers = removeHeaderMapValue(stream.responseTrailers, key) 255 default: 256 panic("unimplemented") 257 } 258 return internal.StatusOK 259 } 260 261 func removeHeaderMapValue(base [][2]string, key string) [][2]string { 262 key = strings.ToLower(key) 263 for i, h := range base { 264 if h[0] == key { 265 if len(base)-1 == i { 266 return base[:i] 267 } else { 268 return append(base[:i], base[i+1:]...) 269 } 270 } 271 } 272 return base 273 } 274 275 // impl internal.ProxyWasmHost: delegated from hostEmulator 276 func (h *httpHostEmulator) httpHostEmulatorProxyGetHeaderMapPairs(mapType internal.MapType, returnValueData **byte, 277 returnValueSize *int) internal.Status { 278 active := internal.VMStateGetActiveContextID() 279 stream := h.httpStreams[active] 280 281 var m []byte 282 switch mapType { 283 case internal.MapTypeHttpRequestHeaders: 284 m = internal.SerializeMap(stream.requestHeaders) 285 case internal.MapTypeHttpResponseHeaders: 286 m = internal.SerializeMap(stream.responseHeaders) 287 case internal.MapTypeHttpRequestTrailers: 288 m = internal.SerializeMap(stream.requestTrailers) 289 case internal.MapTypeHttpResponseTrailers: 290 m = internal.SerializeMap(stream.responseTrailers) 291 default: 292 panic("unreachable: maybe a bug in this host emulation or SDK") 293 } 294 295 if len(m) == 0 { 296 // The host might reutrn OK without setting the data pointer, 297 // if there's nothing to pass to Wasm VM. 298 *returnValueData = nil 299 *returnValueSize = 0 300 return internal.StatusOK 301 } 302 303 *returnValueData = &m[0] 304 *returnValueSize = len(m) 305 return internal.StatusOK 306 } 307 308 // impl internal.ProxyWasmHost 309 func (h *httpHostEmulator) ProxySetHeaderMapPairs(mapType internal.MapType, mapData *byte, mapSize int) internal.Status { 310 m := deserializeRawBytePtrToMap(mapData, mapSize) 311 active := internal.VMStateGetActiveContextID() 312 stream := h.httpStreams[active] 313 314 switch mapType { 315 case internal.MapTypeHttpRequestHeaders: 316 stream.requestHeaders = m 317 case internal.MapTypeHttpResponseHeaders: 318 stream.responseHeaders = m 319 case internal.MapTypeHttpRequestTrailers: 320 stream.requestTrailers = m 321 case internal.MapTypeHttpResponseTrailers: 322 stream.responseTrailers = m 323 default: 324 panic("unimplemented") 325 } 326 return internal.StatusOK 327 } 328 329 // impl internal.ProxyWasmHost 330 func (h *httpHostEmulator) ProxyContinueStream(internal.StreamType) internal.Status { 331 active := internal.VMStateGetActiveContextID() 332 stream := h.httpStreams[active] 333 stream.action = types.ActionContinue 334 return internal.StatusOK 335 } 336 337 // impl internal.ProxyWasmHost 338 func (h *httpHostEmulator) ProxySendLocalResponse(statusCode uint32, 339 statusCodeDetailData *byte, statusCodeDetailsSize int, bodyData *byte, bodySize int, 340 headersData *byte, headersSize int, grpcStatus int32) internal.Status { 341 active := internal.VMStateGetActiveContextID() 342 stream := h.httpStreams[active] 343 stream.sentLocalResponse = &LocalHttpResponse{ 344 StatusCode: statusCode, 345 StatusCodeDetail: internal.RawBytePtrToString(statusCodeDetailData, statusCodeDetailsSize), 346 Data: internal.RawBytePtrToByteSlice(bodyData, bodySize), 347 Headers: deserializeRawBytePtrToMap(headersData, headersSize), 348 GRPCStatus: grpcStatus, 349 } 350 return internal.StatusOK 351 } 352 353 // impl HostEmulator 354 func (h *httpHostEmulator) InitializeHttpContext() (contextID uint32) { 355 contextID = getNextContextID() 356 internal.ProxyOnContextCreate(contextID, PluginContextID) 357 h.httpStreams[contextID] = &httpStreamState{action: types.ActionContinue} 358 return 359 } 360 361 // impl HostEmulator 362 func (h *httpHostEmulator) CallOnRequestHeaders(contextID uint32, headers [][2]string, endOfStream bool) types.Action { 363 cs, ok := h.httpStreams[contextID] 364 if !ok { 365 log.Fatalf("invalid context id: %d", contextID) 366 } 367 368 cs.requestHeaders = cloneWithLowerCaseMapKeys(headers) 369 cs.action = internal.ProxyOnRequestHeaders(contextID, 370 len(headers), endOfStream) 371 return cs.action 372 } 373 374 // impl HostEmulator 375 func (h *httpHostEmulator) CallOnResponseHeaders(contextID uint32, headers [][2]string, endOfStream bool) types.Action { 376 cs, ok := h.httpStreams[contextID] 377 if !ok { 378 log.Fatalf("invalid context id: %d", contextID) 379 } 380 381 cs.responseHeaders = cloneWithLowerCaseMapKeys(headers) 382 cs.action = internal.ProxyOnResponseHeaders(contextID, len(headers), endOfStream) 383 return cs.action 384 } 385 386 // impl HostEmulator 387 func (h *httpHostEmulator) CallOnRequestTrailers(contextID uint32, trailers [][2]string) types.Action { 388 cs, ok := h.httpStreams[contextID] 389 if !ok { 390 log.Fatalf("invalid context id: %d", contextID) 391 } 392 393 cs.requestTrailers = cloneWithLowerCaseMapKeys(trailers) 394 cs.action = internal.ProxyOnRequestTrailers(contextID, len(trailers)) 395 return cs.action 396 } 397 398 // impl HostEmulator 399 func (h *httpHostEmulator) CallOnResponseTrailers(contextID uint32, trailers [][2]string) types.Action { 400 cs, ok := h.httpStreams[contextID] 401 if !ok { 402 log.Fatalf("invalid context id: %d", contextID) 403 } 404 405 cs.responseTrailers = cloneWithLowerCaseMapKeys(trailers) 406 cs.action = internal.ProxyOnResponseTrailers(contextID, len(trailers)) 407 return cs.action 408 } 409 410 // impl HostEmulator 411 func (h *httpHostEmulator) CallOnRequestBody(contextID uint32, body []byte, endOfStream bool) types.Action { 412 cs, ok := h.httpStreams[contextID] 413 if !ok { 414 log.Fatalf("invalid context id: %d", contextID) 415 } 416 417 cs.requestBody = append(cs.requestBodyBuffer, body...) 418 cs.action = internal.ProxyOnRequestBody(contextID, 419 len(body), endOfStream) 420 if cs.action == types.ActionPause { 421 // Buffering requested 422 cs.requestBodyBuffer = cs.requestBody 423 } else { 424 cs.requestBodyBuffer = nil 425 } 426 return cs.action 427 } 428 429 // impl HostEmulator 430 func (h *httpHostEmulator) CallOnResponseBody(contextID uint32, body []byte, endOfStream bool) types.Action { 431 cs, ok := h.httpStreams[contextID] 432 if !ok { 433 log.Fatalf("invalid context id: %d", contextID) 434 } 435 436 cs.responseBody = append(cs.responseBodyBuffer, body...) 437 cs.action = internal.ProxyOnResponseBody(contextID, 438 len(body), endOfStream) 439 if cs.action == types.ActionPause { 440 // Buffering requested 441 cs.responseBodyBuffer = cs.responseBody 442 } else { 443 cs.responseBodyBuffer = nil 444 } 445 return cs.action 446 } 447 448 // impl HostEmulator 449 func (h *httpHostEmulator) CompleteHttpContext(contextID uint32) { 450 internal.ProxyOnLog(contextID) 451 internal.ProxyOnDelete(contextID) 452 } 453 454 // impl HostEmulator 455 func (h *httpHostEmulator) GetCurrentHttpStreamAction(contextID uint32) types.Action { 456 stream, ok := h.httpStreams[contextID] 457 if !ok { 458 log.Fatalf("invalid context id: %d", contextID) 459 } 460 return stream.action 461 } 462 463 // impl HostEmulator 464 func (h *httpHostEmulator) GetCurrentRequestHeaders(contextID uint32) [][2]string { 465 stream, ok := h.httpStreams[contextID] 466 if !ok { 467 log.Fatalf("invalid context id: %d", contextID) 468 } 469 return stream.requestHeaders 470 } 471 472 // impl HostEmulator 473 func (h *httpHostEmulator) GetCurrentResponseHeaders(contextID uint32) [][2]string { 474 stream, ok := h.httpStreams[contextID] 475 if !ok { 476 log.Fatalf("invalid context id: %d", contextID) 477 } 478 return stream.responseHeaders 479 } 480 481 // impl HostEmulator 482 func (h *httpHostEmulator) GetCurrentRequestBody(contextID uint32) []byte { 483 stream, ok := h.httpStreams[contextID] 484 if !ok { 485 log.Fatalf("invalid context id: %d", contextID) 486 } 487 return stream.requestBody 488 } 489 490 // impl HostEmulator 491 func (h *httpHostEmulator) GetCurrentResponseBody(contextID uint32) []byte { 492 stream, ok := h.httpStreams[contextID] 493 if !ok { 494 log.Fatalf("invalid context id: %d", contextID) 495 } 496 return stream.responseBody 497 } 498 499 // impl HostEmulator 500 func (h *httpHostEmulator) GetSentLocalResponse(contextID uint32) *LocalHttpResponse { 501 return h.httpStreams[contextID].sentLocalResponse 502 } 503 504 // impl HostEmulator 505 func (h *httpHostEmulator) GetProperty(path []string) ([]byte, error) { 506 if len(path) == 0 { 507 log.Printf("path must not be empty") 508 return nil, internal.StatusToError(internal.StatusBadArgument) 509 } 510 var ret *byte 511 var retSize int 512 raw := internal.SerializePropertyPath(path) 513 514 err := internal.StatusToError(internal.ProxyGetProperty(&raw[0], len(raw), &ret, &retSize)) 515 if err != nil { 516 return nil, err 517 } 518 return internal.RawBytePtrToByteSlice(ret, retSize), nil 519 } 520 521 // impl HostEmulator 522 func (h *httpHostEmulator) GetPropertyMap(path []string) ([][2]string, error) { 523 b, err := h.GetProperty(path) 524 if err != nil { 525 return nil, err 526 } 527 528 return internal.DeserializeMap(b), nil 529 } 530 531 // impl HostEmulator 532 func (h *httpHostEmulator) SetProperty(path []string, data []byte) error { 533 if len(path) == 0 { 534 log.Printf("path must not be empty") 535 return internal.StatusToError(internal.StatusBadArgument) 536 } else if len(data) == 0 { 537 log.Printf("data must not be empty") 538 return internal.StatusToError(internal.StatusBadArgument) 539 } 540 raw := internal.SerializePropertyPath(path) 541 return internal.StatusToError(internal.ProxySetProperty( 542 &raw[0], len(raw), &data[0], len(data), 543 )) 544 }