t0184-http-proxy-over-p2p.sh 7.9 KB
Newer Older
1 2 3 4 5
#!/usr/bin/env bash

test_description="Test http proxy over p2p"

. lib/test-lib.sh
6 7 8 9 10 11

if ! test_have_prereq SOCAT; then
  skip_all="skipping '$test_description': socat is not available"
  test_done
fi

12
WEB_SERVE_PORT=5099
Steven Allen's avatar
Steven Allen committed
13 14
IPFS_GATEWAY_PORT=5199
SENDER_GATEWAY="http://127.0.0.1:$IPFS_GATEWAY_PORT"
15

16 17 18 19 20
function show_logs() {

    echo "*****************"
    echo "  RECEIVER LOG  "
    echo "*****************"
Steven Allen's avatar
Steven Allen committed
21
    iptb logs 1
22 23 24
    echo "*****************"
    echo "  SENDER LOG  "
    echo "*****************"
Steven Allen's avatar
Steven Allen committed
25
    iptb logs 0
26 27 28 29 30 31 32
    echo "*****************"
    echo "REMOTE_SERVER LOG"
    echo $REMOTE_SERVER_LOG
    echo "*****************"
    cat $REMOTE_SERVER_LOG
}

33
function start_http_server() {
Steven Allen's avatar
Steven Allen committed
34
    REMOTE_SERVER_LOG="server.log"
Jakub Sztandera's avatar
Jakub Sztandera committed
35
    rm -f $REMOTE_SERVER_LOG
36

Jakub Sztandera's avatar
Jakub Sztandera committed
37 38
    touch response
    socat tcp-listen:$WEB_SERVE_PORT,fork,bind=127.0.0.1,reuseaddr 'SYSTEM:cat response'!!CREATE:$REMOTE_SERVER_LOG &
39
    REMOTE_SERVER_PID=$!
40

Jakub Sztandera's avatar
Jakub Sztandera committed
41 42
    socat /dev/null tcp:127.0.01:$WEB_SERVE_PORT,retry=10
    return $?
43 44
}

Dr Ian Preston's avatar
Dr Ian Preston committed
45
function teardown_remote_server() {
46 47 48 49 50 51 52 53 54
    exec 7<&-
    kill $REMOTE_SERVER_PID > /dev/null 2>&1
    wait $REMOTE_SERVER_PID || true
}

function serve_content() {
    local body=$1
    local status_code=${2:-"200 OK"}
    local length=$((1 + ${#body}))
Jakub Sztandera's avatar
Jakub Sztandera committed
55
    echo -e "HTTP/1.1 $status_code\nContent-length: $length\n\n$body" > response
Dr Ian Preston's avatar
Dr Ian Preston committed
56 57
}

58 59
function curl_check_response_code() {
    local expected_status_code=$1
Steven Allen's avatar
Steven Allen committed
60 61
    local path_stub=${2:-p2p/$RECEIVER_ID/http/index.txt}
    local status_code=$(curl -s --write-out %{http_code} --output /dev/null $SENDER_GATEWAY/$path_stub)
62

63
    if [[ "$status_code" -ne "$expected_status_code" ]];
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
    then
        echo "Found status-code "$status_code", expected "$expected_status_code
        return 1
    fi

    return 0
}

function curl_send_proxy_request_and_check_response() {
    local expected_status_code=$1
    local expected_content=$2

    #
    # make a request to SENDER_IPFS via the proxy endpoint
    #
Steven Allen's avatar
Steven Allen committed
79
    CONTENT_PATH="retrieved-file"
Steven Allen's avatar
Steven Allen committed
80
    STATUS_CODE="$(curl -s -o $CONTENT_PATH --write-out %{http_code} $SENDER_GATEWAY/p2p/$RECEIVER_ID/http/index.txt)"
81 82 83 84

    #
    # check status code
    #
Steven Allen's avatar
Steven Allen committed
85
    if [[ "$STATUS_CODE" -ne "$expected_status_code" ]];
86
    then
Dr Ian Preston's avatar
Dr Ian Preston committed
87
        echo -e "Found status-code "$STATUS_CODE", expected "$expected_status_code
Jakub Sztandera's avatar
Jakub Sztandera committed
88
        show_logs
89 90 91 92 93 94
        return 1
    fi

    #
    # check content
    #
Steven Allen's avatar
Steven Allen committed
95
    RESPONSE_CONTENT="$(tail -n 1 $CONTENT_PATH)"
96 97 98 99 100 101 102 103 104
    if [[ "$RESPONSE_CONTENT" == "$expected_content" ]];
    then
        return 0
    else
        echo -e "Found response content:\n'"$RESPONSE_CONTENT"'\nthat differs from expected content:\n'"$expected_content"'"
        return 1
    fi
}

105 106
function curl_send_multipart_form_request() {
    local expected_status_code=$1
Steven Allen's avatar
Steven Allen committed
107
    local FILE_PATH="uploaded-file"
108 109 110 111 112
    FILE_CONTENT="curl will send a multipart-form POST request when sending a file which is handy"
    echo $FILE_CONTENT > $FILE_PATH
    #
    # send multipart form request
    #
113
    STATUS_CODE="$(curl -o /dev/null -s -F file=@$FILE_PATH --write-out %{http_code} $SENDER_GATEWAY/p2p/$RECEIVER_ID/http/index.txt)"
114 115 116
    #
    # check status code
    #
Steven Allen's avatar
Steven Allen committed
117
    if [[ "$STATUS_CODE" -ne "$expected_status_code" ]];
118 119 120 121 122 123 124 125 126 127
    then
        echo -e "Found status-code "$STATUS_CODE", expected "$expected_status_code
        return 1
    fi
    #
    # check request method
    #
    if ! grep "POST /index.txt" $REMOTE_SERVER_LOG > /dev/null;
    then
        echo "Remote server request method/resource path was incorrect"
128
        show_logs
129 130 131 132 133 134 135 136
        return 1
    fi
    #
    # check request is multipart-form
    #
    if ! grep "Content-Type: multipart/form-data;" $REMOTE_SERVER_LOG > /dev/null;
    then
        echo "Request content-type was not multipart/form-data"
137
        show_logs
138 139 140 141
        return 1
    fi
    return 0
}
142

143
test_expect_success 'configure nodes' '
Steven Allen's avatar
Steven Allen committed
144
    iptb testbed create -type localipfs -count 2 -force -init &&
145 146
    ipfsi 0 config --json Experimental.Libp2pStreamMounting true &&
    ipfsi 1 config --json Experimental.Libp2pStreamMounting true &&
147
    ipfsi 0 config --json Experimental.P2pHttpProxy true &&
Steven Allen's avatar
Steven Allen committed
148
    ipfsi 0 config --json Addresses.Gateway "[\"/ip4/127.0.0.1/tcp/$IPFS_GATEWAY_PORT\"]"
149 150
'

151 152 153 154 155 156 157 158 159
test_expect_success 'configure a subdomain gateway with /p2p/ path whitelisted' "
    ipfsi 0 config --json Gateway.PublicGateways '{
        \"example.com\": {
            \"UseSubdomains\": true,
            \"Paths\": [\"/p2p/\"]
        }
    }'
"

160
test_expect_success 'start and connect nodes' '
Steven Allen's avatar
Steven Allen committed
161
    iptb start -wait && iptb connect 0 1
162 163 164
'

test_expect_success 'setup p2p listener on the receiver' '
165 166
    ipfsi 1 p2p listen --allow-custom-protocol /http /ip4/127.0.0.1/tcp/$WEB_SERVE_PORT &&
    ipfsi 1 p2p listen /x/custom/http /ip4/127.0.0.1/tcp/$WEB_SERVE_PORT
167 168
'

Steven Allen's avatar
Steven Allen committed
169
test_expect_success 'setup environment' '
170 171
    RECEIVER_ID=$(ipfsi 1 id -f="<id>" --peerid-base=b58mh)
    RECEIVER_ID_CIDv1=$(ipfsi 1 id -f="<id>" --peerid-base=base36)
172 173
'

174
test_expect_success 'handle proxy http request sends bad-gateway when remote server not available ' '
175
    curl_send_proxy_request_and_check_response 502 ""
176 177
'

178 179 180 181
test_expect_success 'start http server' '
    start_http_server
'

flowed's avatar
flowed committed
182
test_expect_success 'handle proxy http request propagates error response from remote' '
183 184 185 186
    serve_content "SORRY GUYS, I LOST IT" "404 Not Found" &&
    curl_send_proxy_request_and_check_response 404 "SORRY GUYS, I LOST IT"
'

187
test_expect_success 'handle proxy http request ' '
188
    serve_content "THE WOODS ARE LOVELY DARK AND DEEP" &&
189
    curl_send_proxy_request_and_check_response 200 "THE WOODS ARE LOVELY DARK AND DEEP"
190 191 192
'

test_expect_success 'handle proxy http request invalid request' '
Steven Allen's avatar
Steven Allen committed
193
    curl_check_response_code 400 p2p/DERPDERPDERP
194 195 196
'

test_expect_success 'handle proxy http request unknown proxy peer ' '
197 198 199 200 201 202
    UNKNOWN_PEER="k51qzi5uqu5dlmbel1sd8rs4emr3bfosk9bm4eb42514r4lakt4oxw3a3fa2tm" &&
    curl_check_response_code 502 p2p/$UNKNOWN_PEER/http/index.txt
'

test_expect_success 'handle proxy http request to invalid proxy peer ' '
    curl_check_response_code 400 p2p/invalid_peer/http/index.txt
203 204
'

205
test_expect_success 'handle proxy http request to custom protocol' '
206
    serve_content "THE WOODS ARE LOVELY DARK AND DEEP" &&
207 208 209 210
    curl_check_response_code 200 p2p/$RECEIVER_ID/x/custom/http/index.txt
'

test_expect_success 'handle proxy http request to missing protocol' '
211
    serve_content "THE WOODS ARE LOVELY DARK AND DEEP" &&
212 213 214 215 216 217 218
    curl_check_response_code 502 p2p/$RECEIVER_ID/x/missing/http/index.txt
'

test_expect_success 'handle proxy http request missing the /http' '
    curl_check_response_code 400 p2p/$RECEIVER_ID/x/custom/index.txt
'

219
test_expect_success 'handle multipart/form-data http request' '
220 221 222 223
    serve_content "OK" &&
    curl_send_multipart_form_request 200
'

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
# OK: $peerid.p2p.example.com/http/index.txt
test_expect_success "handle http request to a subdomain gateway" '
  serve_content "SUBDOMAIN PROVIDES ORIGIN ISOLATION PER RECEIVER_ID" &&
  curl -H "Host: $RECEIVER_ID_CIDv1.p2p.example.com" -sD - $SENDER_GATEWAY/http/index.txt > p2p_response &&
  test_should_contain "SUBDOMAIN PROVIDES ORIGIN ISOLATION PER RECEIVER_ID" p2p_response
'

# FAIL: $peerid.p2p.example.com/p2p/$peerid/http/index.txt
test_expect_success "handle invalid http request to a subdomain gateway" '
  serve_content "SUBDOMAIN DOES NOT SUPPORT FULL /p2p/ PATH" &&
  curl -H "Host: $RECEIVER_ID_CIDv1.p2p.example.com" -sD - $SENDER_GATEWAY/p2p/$RECEIVER_ID/http/index.txt > p2p_response &&
  test_should_contain "400 Bad Request" p2p_response
'

# REDIRECT: example.com/p2p/$peerid/http/index.txt → $peerid.p2p.example.com/http/index.txt
test_expect_success "redirect http path request to subdomain gateway" '
  serve_content "SUBDOMAIN ROOT REDIRECTS /p2p/ PATH TO SUBDOMAIN" &&
  curl -H "Host: example.com" -sD - $SENDER_GATEWAY/p2p/$RECEIVER_ID/http/index.txt > p2p_response &&
  test_should_contain "Location: http://$RECEIVER_ID_CIDv1.p2p.example.com/http/index.txt" p2p_response
'

245 246
test_expect_success 'stop http server' '
    teardown_remote_server
247 248
'

249 250 251
test_expect_success 'stop nodes' '
    iptb stop
'
252

253
test_done