fn test_respond_to_request_mmdsv2()

in src/mmds/src/lib.rs [469:654]


    fn test_respond_to_request_mmdsv2() {
        // Populate MMDS with data.
        let mmds = populate_mmds();

        // Set version to V2.
        mmds.lock()
            .expect("Poisoned lock")
            .set_version(MmdsVersion::V2)
            .unwrap();
        assert_eq!(
            mmds.lock().expect("Poisoned lock").version(),
            MmdsVersion::V2
        );

        // Test not allowed PATCH HTTP Method.
        let request_bytes = b"PATCH http://169.254.169.255/ HTTP/1.0\r\n\r\n";
        let request = Request::try_from(request_bytes, None).unwrap();
        let mut expected_response = Response::new(Version::Http10, StatusCode::MethodNotAllowed);
        expected_response.set_body(Body::new(Error::MethodNotAllowed.to_string()));
        expected_response.allow_method(Method::Get);
        expected_response.allow_method(Method::Put);
        let actual_response = convert_to_response(request);
        assert_eq!(actual_response, expected_response);

        // Test invalid value for custom header.
        let request_bytes = b"GET http://169.254.169.254/ HTTP/1.0\r\n\
                                    Accept: application/json\r\n
                                    X-metadata-token-ttl-seconds: application/json\r\n\r\n";
        let request = Request::try_from(request_bytes, None).unwrap();
        let mut expected_response = Response::new(Version::Http10, StatusCode::BadRequest);
        expected_response.set_body(Body::new(
            "Invalid header. Reason: Invalid value. \
            Key:X-metadata-token-ttl-seconds; Value:application/json"
                .to_string(),
        ));
        let actual_response = convert_to_response(request);
        assert_eq!(actual_response, expected_response);

        // Test PUT requests.
        // Unsupported `X-Forwarded-For` header present.
        let request_bytes = b"PUT http://169.254.169.254/latest/api/token HTTP/1.0\r\n\
                                    X-Forwarded-For: 203.0.113.195\r\n\r\n";
        let request = Request::try_from(request_bytes, None).unwrap();
        let mut expected_response = Response::new(Version::Http10, StatusCode::BadRequest);
        expected_response.set_body(Body::new(
            "Invalid header. Reason: Unsupported header name. Key: X-Forwarded-For".to_string(),
        ));
        let actual_response = convert_to_response(request);
        assert_eq!(actual_response, expected_response);

        // Test invalid path.
        let request_bytes = b"PUT http://169.254.169.254/token HTTP/1.0\r\n\
                                    X-metadata-token-ttl-seconds: 60\r\n\r\n";
        let request = Request::try_from(request_bytes, None).unwrap();
        let mut expected_response = Response::new(Version::Http10, StatusCode::NotFound);
        expected_response.set_body(Body::new(
            Error::ResourceNotFound(String::from("/token")).to_string(),
        ));
        let actual_response = convert_to_response(request);
        assert_eq!(actual_response, expected_response);

        // Test invalid lifetime values for token.
        let invalid_values = [MIN_TOKEN_TTL_SECONDS - 1, MAX_TOKEN_TTL_SECONDS + 1];
        for invalid_value in invalid_values.iter() {
            let request_bytes = format!(
                "PUT http://169.254.169.254/latest/api/token HTTP/1.0\r\n\
                X-metadata-token-ttl-seconds: {}\r\n\r\n",
                invalid_value
            );
            let request = Request::try_from(request_bytes.as_bytes(), None).unwrap();
            let mut expected_response = Response::new(Version::Http10, StatusCode::BadRequest);
            let error_msg = format!(
                "Invalid time to live value provided for token: {}. \
                Please provide a value between {} and {}.",
                invalid_value, MIN_TOKEN_TTL_SECONDS, MAX_TOKEN_TTL_SECONDS
            );
            expected_response.set_body(Body::new(error_msg));
            let actual_response = convert_to_response(request);
            assert_eq!(actual_response, expected_response);
        }

        // Test no lifetime value provided for token.
        let request_bytes = b"PUT http://169.254.169.254/latest/api/token HTTP/1.0\r\n\r\n";
        let request = Request::try_from(request_bytes, None).unwrap();
        let mut expected_response = Response::new(Version::Http10, StatusCode::BadRequest);
        expected_response.set_body(Body::new(Error::NoTtlProvided.to_string()));
        let actual_response = convert_to_response(request);
        assert_eq!(actual_response, expected_response);

        // Test valid PUT.
        let request_bytes = b"PUT http://169.254.169.254/latest/api/token HTTP/1.0\r\n\
                                    X-metadata-token-ttl-seconds: 60\r\n\r\n";
        let request = Request::try_from(request_bytes, None).unwrap();
        let actual_response = convert_to_response(request);
        assert_eq!(actual_response.status(), StatusCode::OK);
        assert_eq!(actual_response.content_type(), MediaType::PlainText);

        // Test valid GET.
        let valid_token = String::from_utf8(actual_response.body().unwrap().body).unwrap();
        let request_bytes = format!(
            "GET http://169.254.169.254/ HTTP/1.0\r\n\
            Accept: application/json\r\n\
            X-metadata-token: {}\r\n\r\n",
            valid_token
        );
        let request = Request::try_from(request_bytes.as_bytes(), None).unwrap();
        let mut expected_response = Response::new(Version::Http10, StatusCode::OK);
        let mut body = get_json_data().to_string();
        body.retain(|c| !c.is_whitespace());
        expected_response.set_body(Body::new(body));
        let actual_response = convert_to_response(request);
        assert_eq!(actual_response, expected_response);

        // Test GET request towards unsupported value type.
        let request_bytes = format!(
            "GET /age HTTP/1.1\r\n\
            X-metadata-token: {}\r\n\r\n",
            valid_token
        );
        let request = Request::try_from(request_bytes.as_bytes(), None).unwrap();
        let mut expected_response = Response::new(Version::Http11, StatusCode::NotImplemented);
        let body = "Cannot retrieve value. The value has an unsupported type.".to_string();
        expected_response.set_body(Body::new(body));
        let actual_response = convert_to_response(request);
        assert_eq!(actual_response, expected_response);

        // Test GET request towards invalid resource.
        let request_bytes = format!(
            "GET http://169.254.169.254/invalid HTTP/1.0\r\n\
            X-metadata-token: {}\r\n\r\n",
            valid_token
        );
        let request = Request::try_from(request_bytes.as_bytes(), None).unwrap();
        let mut expected_response = Response::new(Version::Http10, StatusCode::NotFound);
        expected_response.set_body(Body::new(
            Error::ResourceNotFound(String::from("/invalid")).to_string(),
        ));
        let actual_response = convert_to_response(request);
        assert_eq!(actual_response, expected_response);

        // Test GET request without token should return Unauthorized status code.
        let request_bytes = b"GET http://169.254.169.254/ HTTP/1.0\r\n\r\n";
        let request = Request::try_from(request_bytes, None).unwrap();
        let mut expected_response = Response::new(Version::Http10, StatusCode::Unauthorized);
        expected_response.set_body(Body::new(Error::NoTokenProvided.to_string()));
        let actual_response = convert_to_response(request);
        assert_eq!(actual_response, expected_response);

        // Test GET request with invalid token should return Unauthorized status code.
        let request_bytes = b"GET http://169.254.169.254/ HTTP/1.0\r\n\
                                    X-metadata-token: foo\r\n\r\n";
        let request = Request::try_from(request_bytes, None).unwrap();
        let mut expected_response = Response::new(Version::Http10, StatusCode::Unauthorized);
        expected_response.set_body(Body::new(Error::InvalidToken.to_string()));
        let actual_response = convert_to_response(request);
        assert_eq!(actual_response, expected_response);

        // Create a new MMDS token that expires in one second.
        let request_bytes = b"PUT http://169.254.169.254/latest/api/token HTTP/1.0\r\n\
                                    X-metadata-token-ttl-seconds: 1\r\n\r\n";
        let request = Request::try_from(request_bytes, None).unwrap();
        let actual_response = convert_to_response(request);
        assert_eq!(actual_response.status(), StatusCode::OK);
        assert_eq!(actual_response.content_type(), MediaType::PlainText);

        // Test GET request with invalid tokens.
        // `valid_token` will become invalid after one second, when it expires.
        let valid_token = String::from_utf8(actual_response.body().unwrap().body).unwrap();
        let invalid_token = std::iter::repeat("a").take(58).collect::<String>();
        let tokens = [invalid_token, valid_token];
        for token in tokens.iter() {
            let request_bytes = format!(
                "GET http://169.254.169.254/ HTTP/1.0\r\n\
                X-metadata-token: {}\r\n\r\n",
                token
            );
            let request = Request::try_from(request_bytes.as_bytes(), None).unwrap();
            let mut expected_response = Response::new(Version::Http10, StatusCode::Unauthorized);
            expected_response.set_body(Body::new(Error::InvalidToken.to_string()));
            let actual_response = convert_to_response(request);
            assert_eq!(actual_response, expected_response);

            // Wait for the second token to expire.
            std::thread::sleep(Duration::from_secs(1));
        }
    }