Skip to content

[THREESCALE-9542] Part 2: Add support to proxy request with Transfer-Encoding: chunked - #1403

Merged
tkan145 merged 8 commits into
3scale:masterfrom
tkan145:THREESCALE-9542-chunked-request
Jan 22, 2024
Merged

[THREESCALE-9542] Part 2: Add support to proxy request with Transfer-Encoding: chunked#1403
tkan145 merged 8 commits into
3scale:masterfrom
tkan145:THREESCALE-9542-chunked-request

Conversation

@tkan145

@tkan145 tkan145 commented Jun 1, 2023

Copy link
Copy Markdown
Contributor

What:

Fix https://issues.redhat.com/browse/THREESCALE-9542

This PR adds support to proxy the request with "Transfer-Encoding: chunked" when using with the proxy server.

Note to reviewers

Please just review the last 2 commits. I will rebase once part 1 merged

Verification steps:

  • Checkout this branch

  • Make runtime-image IMAGE_NAME=apicast-test

make runtime-image IMAGE_NAME=apicast-test
  • Then run the gateway with the built image
diff --git a/dev-environments/https-proxy-upstream-tlsv1.3/apicast-config.json b/dev-environments/https-proxy-upstream-tlsv1.3/apicast-config.json
index 5227c5aa..24c45338 100644
--- a/dev-environments/https-proxy-upstream-tlsv1.3/apicast-config.json
+++ b/dev-environments/https-proxy-upstream-tlsv1.3/apicast-config.json
@@ -44,6 +44,11 @@
           "host": "backend"
         },
         "policy_chain": [
+          {
+              "name": "request_unbuffered",
+              "version": "builtin",
+              "configuration": {}
+          },
           {
             "name": "apicast.policy.http_proxy",
             "configuration": {
cd dev-environments/https-proxy-upstream-tlsv1.3
make certs
make gateway IMAGE_NAME=apicast-test
  • Send chunked request with one chunk body
curl --resolve post.example.com:8080:127.0.0.1 -v -H "Transfer-Encoding: chunked"   -H "Content-Type: application/json"  -d @my-data.json "http://post.example.com:8080/?user_key=123"

The request should return 200 OK. Note that upstream echo API is reporting that the request included Transfer-Encoding: chunked header and the expected body.

Details
 ▲  curl --resolve post.example.com:8080:127.0.0.1 -v -H "Transfer-Encoding: chunked"   -H "Content-Type: application/json"  -d 'hello, world' "http://post.example.com:8080/?user_key=123"
* Added post.example.com:8080:127.0.0.1 to DNS cache
* Hostname post.example.com was found in DNS cache
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to post.example.com (127.0.0.1) port 8080 (#0)
> POST /?user_key=123 HTTP/1.1
> Host: post.example.com:8080
> User-Agent: curl/7.61.1
> Accept: */*
> Transfer-Encoding: chunked
> Content-Type: application/json
>
> c
* upload completely sent off: 19 out of 12 bytes
< HTTP/1.1 200 OK
< {
<   "args": {
<     "user_key": "123"
<   },
<   "data": "hello, world",
<   "files": {},
<   "form": {},
<   "headers": {
<     "Accept": "*/*",
<     "Content-Type": "application/json",
<     "Host": "example.com",
<     "Transfer-Encoding": "chunked",
<     "User-Agent": "curl/7.61.1"
<   },
<   "json": null,
<   "origin": "172.25.0.2",
<   "url": "http://example.com/post?user_key=123"
< }
* Connection #0 to host post.example.com left intact
  • Send chunked request with few chunks in the body delayed in time. Python3 is required.

First get the APICast IPAddress

 ▲ docker inspect https-proxy-upstream-tlsv13-gateway-run-d76ff72726ec | grep IPAddress
cat <<EOF >chunked-request.py
import http.client
import time

def gen():
    yield bytes('hi', "utf-8")
    time.sleep(2)
    yield bytes('there', "utf-8")
    time.sleep(2)
    yield bytes('bye', "utf-8")

http.client.HTTPConnection.debuglevel = 1
conn = http.client.HTTPConnection('127.0.0.1', 8080)

headers = {'Content-type': 'application/octet-stream', 'Host': 'post.example.com'}

conn.request('POST', '/?user_key=foo', gen(), headers)

response = conn.getresponse()
print(response.read().decode())
EOF

Replace 127.0.0.1 with the IP of APIcast gateway above

> python3 chunked-request.py
send: b'POST /?user_key=foo HTTP/1.1\r\nAccept-Encoding: identity\r\nTransfer-Encoding: chunked\r\nContent-type: application/octet-stream\r\nHost: post.example.com\r\n\r\n'
send: b'2\r\nhi\r\n'                                                                                                                                                        
send: b'5\r\nthere\r\n'                                                                                                                                                     
send: b'3\r\nbye\r\n'                                                                                                                                                       
send: b'0\r\n\r\n'                                                                                                                                                          
reply: 'HTTP/1.1 200 OK\r\n'                                                                                                                                                
header: Access-Control-Allow-Credentials: true                                                                                                                              
header: Access-Control-Allow-Origin: *                                                                                                                                      
header: Date: Tue, 09 Jan 2024 03:40:18 GMT                                                                                                                                 
header: Content-Type: application/json                                                                                                                                      
header: Server: gunicorn/19.9.0                                                                                                                                             
{
  "args": {
    "user_key": "foo"
  }, 
  "data": "hitherebye", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "identity", 
    "Content-Type": "application/octet-stream", 
    "Host": "example.com", 
    "Transfer-Encoding": "chunked",
    "User-Agent": "lua-resty-http/0.14 (Lua) ngx_lua/10019"
  }, 
  "json": null, 
  "origin": "172.18.0.4"
  "url": "http://example.com/post?user_key=foo"
}
  • Note that the upstream service got transfer encoding chunked request and chunked encoding of the request body with the length bytes preceding each chunk.
Details
> 2024/01/09 03:40:14.000960414  length=203 from=0 to=202 
POST /post?user_key=foo HTTP/1.1\r                        
User-Agent: lua-resty-http/0.14 (Lua) ngx_lua/10019\r     
Transfer-Encoding: chunked\r                              
Host: example.com\r                                       
Accept-Encoding: identity\r                               
Content-type: application/octet-stream\r                  
\r                                                        
> 2024/01/09 03:40:14.000960570  length=7 from=203 to=209 
2\r
hi\r
> 2024/01/09 03:40:16.000952052  length=10 from=210 to=219
5\r
there\r
> 2024/01/09 03:40:18.000954073  length=8 from=220 to=227
3\r
bye\r
> 2024/01/09 03:40:18.000954120  length=5 from=228 to=232
0\r
\r
< 2024/01/09 03:40:18.000954722  length=653 from=0 to=652
HTTP/1.1 200 OK\r
Server: gunicorn/19.9.0\r
Date: Tue, 09 Jan 2024 03:40:18 GMT\r
Connection: keep-alive\r
Content-Type: application/json\r
Content-Length: 423\r
Access-Control-Allow-Origin: *\r
Access-Control-Allow-Credentials: true\r
\r
{
  "args": {
    "user_key": "foo"
  }, 
  "data": "hitherebye", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "identity", 
    "Content-Type": "application/octet-stream", 
    "Host": "example.com", 
    "Transfer-Encoding": "chunked", 
    "User-Agent": "lua-resty-http/0.14 (Lua) ngx_lua/10019"
  }, 
  "json": null, 
  "origin": "172.18.0.4", 
  "url": "http://example.com/post?user_key=foo"
}
  • Send chunked request with expect 100-continue header.
cat <<EOF >chunked-request.py
import http.client
import time

def gen():
    yield bytes('hi', "utf-8")
    time.sleep(2)
    yield bytes('there', "utf-8")
    time.sleep(2)
    yield bytes('bye', "utf-8")

http.client.HTTPConnection.debuglevel = 1
conn = http.client.HTTPConnection('127.0.0.1', 8080)

headers = {'Content-type': 'application/octet-stream', 'Host': 'post.example.com', 'Expect': '100-continue'}

conn.request('POST', '/?user_key=foo', gen(), headers)

response = conn.getresponse()
print(response.read().decode())
EOF
  • Note that the upstream service got transfer encoding chunked request and return 100 Continue
▲ python3 ./chunked-request.py

send: b'POST /?user_key=foo HTTP/1.1\r\nAccept-Encoding: identity\r\nTransfer-Encoding: chunked\r\nContent-type: application/octet-stream\r\nHost: post.example.com\r\nExpect: 100-continue\r\n\r\n'
send: b'2\r\nhi\r\n'                          
send: b'5\r\nthere\r\n'                       
send: b'3\r\nbye\r\n'                         
send: b'0\r\n\r\n'                            
reply: 'HTTP/1.1 100 Continue\r\n'            
headers: [b'\r\n']                            
reply: 'HTTP/1.1 200 OK\r\n'                  
header: Access-Control-Allow-Credentials: true
header: Access-Control-Allow-Origin: *        
header: Date: Tue, 09 Jan 2024 03:47:02 GMT   
header: Content-Type: application/json        
header: Server: gunicorn/19.9.0
{                                                          
  "args": {                                                
    "user_key": "foo"                                      
  },                                                       
  "data": "hitherebye",                                    
  "files": {},                                             
  "form": {},                                              
  "headers": {                                             
    "Accept-Encoding": "identity",                         
    "Content-Type": "application/octet-stream",            
    "Expect": "100-continue",                              
    "Host": "example.com",                                 
    "Transfer-Encoding": "chunked",                        
    "User-Agent": "lua-resty-http/0.14 (Lua) ngx_lua/10019"
  },                                                       
  "json": null,                                            
  "origin": "172.18.0.4",                                  
  "url": "http://example.com/post?user_key=foo"            
}

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants