diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/CuTest.c | 13 | ||||
-rw-r--r-- | test/CuTest.h | 5 | ||||
-rw-r--r-- | test/serf_get.c | 40 | ||||
-rw-r--r-- | test/server/serfcacert.pem | 50 | ||||
-rw-r--r-- | test/server/serfrootcacert.pem | 60 | ||||
-rw-r--r-- | test/server/serfservercert.pem | 50 | ||||
-rw-r--r-- | test/server/serfserverkey.pem | 18 | ||||
-rw-r--r-- | test/server/test_server.c | 171 | ||||
-rw-r--r-- | test/server/test_server.h | 94 | ||||
-rw-r--r-- | test/server/test_sslserver.c | 252 | ||||
-rw-r--r-- | test/test_buckets.c | 332 | ||||
-rw-r--r-- | test/test_context.c | 850 | ||||
-rw-r--r-- | test/test_serf.h | 22 | ||||
-rw-r--r-- | test/test_ssl.c | 21 | ||||
-rw-r--r-- | test/test_util.c | 129 |
15 files changed, 1698 insertions, 409 deletions
diff --git a/test/CuTest.c b/test/CuTest.c index 315f59f..377d396 100644 --- a/test/CuTest.c +++ b/test/CuTest.c @@ -205,13 +205,14 @@ void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, CuFail_Line(tc, file, line, NULL, message); } -void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, - const char* expected, const char* actual) +void CuAssertStrnEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, + const char* expected, size_t explen, + const char* actual) { CuString string; if ((expected == NULL && actual == NULL) || (expected != NULL && actual != NULL && - strcmp(expected, actual) == 0)) + strncmp(expected, actual, explen) == 0)) { return; } @@ -230,6 +231,12 @@ void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const cha CuFailInternal(tc, file, line, &string); } +void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, + const char* expected, const char* actual) +{ + CuAssertStrnEquals_LineMsg(tc, file, line, message, expected, strlen(expected), actual); +} + void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, int expected, int actual) { diff --git a/test/CuTest.h b/test/CuTest.h index 1895bec..930ae9c 100644 --- a/test/CuTest.h +++ b/test/CuTest.h @@ -90,6 +90,9 @@ void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, const char* expected, const char* actual); +void CuAssertStrnEquals_LineMsg(CuTest* tc, + const char* file, int line, const char* message, + const char* expected, size_t explen, const char* actual); void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, int expected, int actual); @@ -108,6 +111,8 @@ void CuAssertPtrEquals_LineMsg(CuTest* tc, #define CuAssertStrEquals(tc,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) #define CuAssertStrEquals_Msg(tc,ms,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) +#define CuAssertStrnEquals(tc,ex,exlen,ac) CuAssertStrnEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(exlen),(ac)) +#define CuAssertStrnEquals_Msg(tc,ms,ex,exlen,ac) CuAssertStrnEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(exlen),ac)) #define CuAssertIntEquals(tc,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) #define CuAssertIntEquals_Msg(tc,ms,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) #define CuAssertDblEquals(tc,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac),(dl)) diff --git a/test/serf_get.c b/test/serf_get.c index ac1bb8e..b63bb66 100644 --- a/test/serf_get.c +++ b/test/serf_get.c @@ -68,6 +68,18 @@ static apr_status_t ignore_all_cert_errors(void *data, int failures, return APR_SUCCESS; } +static char * +convert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool) +{ + return apr_psprintf(pool, "%s, %s, %s, %s, %s (%s)", + (char*)apr_hash_get(org, "OU", APR_HASH_KEY_STRING), + (char*)apr_hash_get(org, "O", APR_HASH_KEY_STRING), + (char*)apr_hash_get(org, "L", APR_HASH_KEY_STRING), + (char*)apr_hash_get(org, "ST", APR_HASH_KEY_STRING), + (char*)apr_hash_get(org, "C", APR_HASH_KEY_STRING), + (char*)apr_hash_get(org, "E", APR_HASH_KEY_STRING)); +} + static apr_status_t print_certs(void *data, int failures, int error_depth, const serf_ssl_certificate_t * const * certs, apr_size_t certs_len) @@ -86,8 +98,34 @@ static apr_status_t print_certs(void *data, int failures, int error_depth, fprintf(stderr, "Chain provided with depth=%d\n", error_depth); while ((current = *certs) != NULL) - { + { + apr_hash_t *issuer, *subject, *serf_cert; + apr_array_header_t *san; + + subject = serf_ssl_cert_subject(current, pool); + issuer = serf_ssl_cert_issuer(current, pool); + serf_cert = serf_ssl_cert_certificate(current, pool); + fprintf(stderr, "\n-----BEGIN CERTIFICATE-----\n"); + fprintf(stderr, "Hostname: %s\n", + (const char *)apr_hash_get(subject, "CN", APR_HASH_KEY_STRING)); + fprintf(stderr, "Sha1: %s\n", + (const char *)apr_hash_get(serf_cert, "sha1", APR_HASH_KEY_STRING)); + fprintf(stderr, "Valid from: %s\n", + (const char *)apr_hash_get(serf_cert, "notBefore", APR_HASH_KEY_STRING)); + fprintf(stderr, "Valid until: %s\n", + (const char *)apr_hash_get(serf_cert, "notAfter", APR_HASH_KEY_STRING)); + fprintf(stderr, "Issuer: %s\n", convert_organisation_to_str(issuer, pool)); + + san = apr_hash_get(serf_cert, "subjectAltName", APR_HASH_KEY_STRING); + if (san) { + int i; + for (i = 0; i < san->nelts; i++) { + char *s = APR_ARRAY_IDX(san, i, char*); + fprintf(stderr, "SubjectAltName: %s\n", s); + } + } + fprintf(stderr, "%s\n", serf_ssl_cert_export(current, pool)); fprintf(stderr, "-----END CERTIFICATE-----\n"); ++certs; diff --git a/test/server/serfcacert.pem b/test/server/serfcacert.pem new file mode 100644 index 0000000..f8324cd --- /dev/null +++ b/test/server/serfcacert.pem @@ -0,0 +1,50 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 1048578 (0x100002) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=BE, ST=Antwerp, L=Mechelen, O=In Serf we trust, Inc., OU=Test Suite Root CA, CN=Serf Root CA/emailAddress=serfrootca@example.com + Validity + Not Before: Apr 13 11:28:06 2013 GMT + Not After : Apr 13 11:28:06 2014 GMT + Subject: C=BE, ST=Antwerp, L=Mechelen, O=In Serf we trust, Inc., OU=Test Suite CA, CN=Serf CA/emailAddress=serfca@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:ab:10:e4:6c:e6:31:23:9f:f3:5d:a9:22:12:81: + 9a:b5:8d:7b:4d:8e:b7:8c:1f:2e:14:81:03:bb:44: + 50:79:d0:42:8c:f4:0f:86:f1:00:76:03:87:ac:a0: + a2:3a:6f:ab:b5:17:81:ce:86:d4:35:70:4c:a1:cb: + 2c:67:17:9b:06:b3:4f:1e:18:ce:9b:5d:15:8e:10: + 24:a5:9c:73:43:fe:b4:68:a8:65:50:58:31:3b:df: + b7:f8:33:d0:5d:af:c6:b1:ec:ed:73:09:cd:3e:42: + 6a:95:13:d0:bd:b8:9d:5a:23:24:fc:34:4f:b0:72: + 4b:15:e5:b4:13:4b:bc:24:85 + Exponent: 65537 (0x10001) + Signature Algorithm: sha1WithRSAEncryption + 46:db:5f:de:b5:66:f8:9b:04:da:3c:f5:1a:99:98:c8:1f:57: + f8:1e:7a:f9:b4:1e:7e:21:97:11:70:64:2c:58:90:00:28:09: + 12:10:81:fb:27:01:37:4b:22:9a:56:95:42:e2:eb:4b:b5:8b: + bd:9f:09:4b:34:15:2f:ce:1e:dd:71:8c:40:0c:8a:6a:fe:a6: + 12:f1:68:b7:f3:be:e4:69:fb:b6:3b:c8:c4:13:9c:ae:09:d1: + dc:51:b9:77:90:14:2b:65:83:30:12:76:ef:33:24:65:99:63: + 2e:62:d7:21:db:e5:d9:a6:2d:05:17:a1:5e:ef:6f:26:f6:2f: + 96:b4 +-----BEGIN CERTIFICATE----- +MIICwTCCAioCAxAAAjANBgkqhkiG9w0BAQUFADCBrjELMAkGA1UEBhMCQkUxEDAO +BgNVBAgMB0FudHdlcnAxETAPBgNVBAcMCE1lY2hlbGVuMR8wHQYDVQQKDBZJbiBT +ZXJmIHdlIHRydXN0LCBJbmMuMRswGQYDVQQLDBJUZXN0IFN1aXRlIFJvb3QgQ0Ex +FTATBgNVBAMMDFNlcmYgUm9vdCBDQTElMCMGCSqGSIb3DQEJARYWc2VyZnJvb3Rj +YUBleGFtcGxlLmNvbTAeFw0xMzA0MTMxMTI4MDZaFw0xNDA0MTMxMTI4MDZaMIGg +MQswCQYDVQQGEwJCRTEQMA4GA1UECAwHQW50d2VycDERMA8GA1UEBwwITWVjaGVs +ZW4xHzAdBgNVBAoMFkluIFNlcmYgd2UgdHJ1c3QsIEluYy4xFjAUBgNVBAsMDVRl +c3QgU3VpdGUgQ0ExEDAOBgNVBAMMB1NlcmYgQ0ExITAfBgkqhkiG9w0BCQEWEnNl +cmZjYUBleGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqxDk +bOYxI5/zXakiEoGatY17TY63jB8uFIEDu0RQedBCjPQPhvEAdgOHrKCiOm+rtReB +zobUNXBMocssZxebBrNPHhjOm10VjhAkpZxzQ/60aKhlUFgxO9+3+DPQXa/Gsezt +cwnNPkJqlRPQvbidWiMk/DRPsHJLFeW0E0u8JIUCAwEAATANBgkqhkiG9w0BAQUF +AAOBgQBG21/etWb4mwTaPPUamZjIH1f4Hnr5tB5+IZcRcGQsWJAAKAkSEIH7JwE3 +SyKaVpVC4utLtYu9nwlLNBUvzh7dcYxADIpq/qYS8Wi3877kafu2O8jEE5yuCdHc +Ubl3kBQrZYMwEnbvMyRlmWMuYtch2+XZpi0FF6Fe728m9i+WtA== +-----END CERTIFICATE----- diff --git a/test/server/serfrootcacert.pem b/test/server/serfrootcacert.pem new file mode 100644 index 0000000..440f39f --- /dev/null +++ b/test/server/serfrootcacert.pem @@ -0,0 +1,60 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 14060582211199810902 (0xc321390661bdbd56) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=BE, ST=Antwerp, L=Mechelen, O=In Serf we trust, Inc., OU=Test Suite Root CA, CN=Serf Root CA/emailAddress=serfrootca@example.com + Validity + Not Before: Apr 13 11:19:14 2013 GMT + Not After : Apr 11 11:19:14 2023 GMT + Subject: C=BE, ST=Antwerp, L=Mechelen, O=In Serf we trust, Inc., OU=Test Suite Root CA, CN=Serf Root CA/emailAddress=serfrootca@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:e1:dd:69:ea:ac:fd:f2:73:81:ec:ed:b6:b1:0e: + 70:23:8c:01:6d:ab:f3:43:ab:0f:fc:8a:6a:23:eb: + 6c:48:37:c9:c0:8f:29:61:00:7e:89:1f:00:d1:68: + dd:70:de:bd:34:32:0e:41:ac:f9:ea:c1:a6:0d:b5: + 65:be:5c:9e:f1:b4:27:54:c1:79:61:63:d4:2d:06: + 11:5f:cc:4c:d9:d3:ef:4e:da:9f:a4:26:16:cb:3f: + 86:f8:21:7d:c5:3a:32:34:c8:cb:85:ad:c4:3f:e4: + b3:ad:8e:a7:67:9e:0c:3b:5a:58:29:5f:ce:96:3b: + e3:f5:ca:42:eb:7b:44:d5:75 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + B2:3E:19:35:C1:C4:4F:23:79:ED:BF:E8:DC:5C:31:03:F0:2F:15:77 + X509v3 Authority Key Identifier: + keyid:B2:3E:19:35:C1:C4:4F:23:79:ED:BF:E8:DC:5C:31:03:F0:2F:15:77 + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + c3:ec:1f:3e:b1:87:d0:80:10:f9:bf:73:1b:38:d4:b1:b7:80: + 4d:ea:20:c1:79:7d:f5:58:42:11:13:28:ab:b1:b4:0a:88:9c: + 20:4d:9c:b5:5a:41:28:5e:f6:69:5e:55:bb:e2:1a:b9:c6:62: + 38:86:32:7b:93:28:ca:9e:af:d1:06:f9:93:c2:5d:92:c0:25: + 68:6a:e1:fe:85:2a:19:a7:6b:17:4d:23:9a:72:d6:d0:c1:80: + ff:74:10:8b:62:7a:11:c3:9a:87:2a:e4:7d:d1:8c:72:a6:bf: + c1:3b:d8:b8:33:c0:ff:b0:f7:d6:0e:a3:dd:36:fe:8a:41:a0: + 98:cc +-----BEGIN CERTIFICATE----- +MIIDLDCCApWgAwIBAgIJAMMhOQZhvb1WMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD +VQQGEwJCRTEQMA4GA1UECAwHQW50d2VycDERMA8GA1UEBwwITWVjaGVsZW4xHzAd +BgNVBAoMFkluIFNlcmYgd2UgdHJ1c3QsIEluYy4xGzAZBgNVBAsMElRlc3QgU3Vp +dGUgUm9vdCBDQTEVMBMGA1UEAwwMU2VyZiBSb290IENBMSUwIwYJKoZIhvcNAQkB +FhZzZXJmcm9vdGNhQGV4YW1wbGUuY29tMB4XDTEzMDQxMzExMTkxNFoXDTIzMDQx +MTExMTkxNFowga4xCzAJBgNVBAYTAkJFMRAwDgYDVQQIDAdBbnR3ZXJwMREwDwYD +VQQHDAhNZWNoZWxlbjEfMB0GA1UECgwWSW4gU2VyZiB3ZSB0cnVzdCwgSW5jLjEb +MBkGA1UECwwSVGVzdCBTdWl0ZSBSb290IENBMRUwEwYDVQQDDAxTZXJmIFJvb3Qg +Q0ExJTAjBgkqhkiG9w0BCQEWFnNlcmZyb290Y2FAZXhhbXBsZS5jb20wgZ8wDQYJ +KoZIhvcNAQEBBQADgY0AMIGJAoGBAOHdaeqs/fJzgezttrEOcCOMAW2r80OrD/yK +aiPrbEg3ycCPKWEAfokfANFo3XDevTQyDkGs+erBpg21Zb5cnvG0J1TBeWFj1C0G +EV/MTNnT707an6QmFss/hvghfcU6MjTIy4WtxD/ks62Op2eeDDtaWClfzpY74/XK +Qut7RNV1AgMBAAGjUDBOMB0GA1UdDgQWBBSyPhk1wcRPI3ntv+jcXDED8C8VdzAf +BgNVHSMEGDAWgBSyPhk1wcRPI3ntv+jcXDED8C8VdzAMBgNVHRMEBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4GBAMPsHz6xh9CAEPm/cxs41LG3gE3qIMF5ffVYQhETKKux +tAqInCBNnLVaQShe9mleVbviGrnGYjiGMnuTKMqer9EG+ZPCXZLAJWhq4f6FKhmn +axdNI5py1tDBgP90EItiehHDmocq5H3RjHKmv8E72LgzwP+w99YOo902/opBoJjM +-----END CERTIFICATE----- diff --git a/test/server/serfservercert.pem b/test/server/serfservercert.pem new file mode 100644 index 0000000..086e0e5 --- /dev/null +++ b/test/server/serfservercert.pem @@ -0,0 +1,50 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 1048579 (0x100003) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=BE, ST=Antwerp, L=Mechelen, O=In Serf we trust, Inc., OU=Test Suite CA, CN=Serf CA/emailAddress=serfca@example.com + Validity + Not Before: Apr 13 11:41:01 2013 GMT + Not After : Apr 13 11:41:01 2014 GMT + Subject: C=BE, ST=Antwerp, L=Mechelen, O=In Serf we trust, Inc., OU=Test Suite Server, CN=Serf Server/emailAddress=serfserver@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:aa:8d:56:e7:0d:99:03:9b:3f:b3:c0:2a:63:33: + 51:65:4b:e7:d2:6c:60:d2:52:d7:8a:6d:c9:8b:93: + 3d:4c:1a:eb:29:26:9c:19:dc:a5:a6:70:3f:0b:a7: + 69:4d:8b:35:00:b6:8f:f2:4f:b6:38:43:b9:a7:c8: + 2d:51:7f:f2:f7:00:28:48:b8:f7:9a:7d:a5:7d:5c: + 17:f7:d0:14:54:86:39:88:43:1d:5c:d8:d4:56:9f: + 88:a8:3c:47:7f:65:cf:94:56:b0:d8:b6:dd:25:66: + 34:ba:cb:43:cd:df:93:ce:27:1b:57:7f:8a:50:f5: + 5a:33:d3:55:52:ff:9e:f7:4f + Exponent: 65537 (0x10001) + Signature Algorithm: sha1WithRSAEncryption + 82:02:80:89:ec:c2:56:51:1f:d1:f6:2f:8c:d9:50:49:c2:7f: + 00:76:53:65:15:96:bb:0f:7e:b3:5b:06:c2:9c:44:77:34:e9: + 64:41:ca:cd:aa:41:4a:6f:b0:95:cb:41:11:1b:75:0e:5e:d6: + 34:80:8f:22:a9:e4:ce:cd:9a:b6:0c:96:7e:e6:2a:f0:ce:eb: + 86:7a:2b:df:b8:13:3a:c8:b5:9d:5b:71:9c:40:61:56:c6:f9: + 90:b8:4f:1d:14:88:b2:ad:40:08:f5:88:ab:10:5d:74:6d:a6: + 29:3d:1b:01:68:9f:29:48:24:d9:3d:52:47:ca:bf:22:af:fd: + 7b:df +-----BEGIN CERTIFICATE----- +MIICvzCCAigCAxAAAzANBgkqhkiG9w0BAQUFADCBoDELMAkGA1UEBhMCQkUxEDAO +BgNVBAgMB0FudHdlcnAxETAPBgNVBAcMCE1lY2hlbGVuMR8wHQYDVQQKDBZJbiBT +ZXJmIHdlIHRydXN0LCBJbmMuMRYwFAYDVQQLDA1UZXN0IFN1aXRlIENBMRAwDgYD +VQQDDAdTZXJmIENBMSEwHwYJKoZIhvcNAQkBFhJzZXJmY2FAZXhhbXBsZS5jb20w +HhcNMTMwNDEzMTE0MTAxWhcNMTQwNDEzMTE0MTAxWjCBrDELMAkGA1UEBhMCQkUx +EDAOBgNVBAgMB0FudHdlcnAxETAPBgNVBAcMCE1lY2hlbGVuMR8wHQYDVQQKDBZJ +biBTZXJmIHdlIHRydXN0LCBJbmMuMRowGAYDVQQLDBFUZXN0IFN1aXRlIFNlcnZl +cjEUMBIGA1UEAwwLU2VyZiBTZXJ2ZXIxJTAjBgkqhkiG9w0BCQEWFnNlcmZzZXJ2 +ZXJAZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKqNVucN +mQObP7PAKmMzUWVL59JsYNJS14ptyYuTPUwa6ykmnBncpaZwPwunaU2LNQC2j/JP +tjhDuafILVF/8vcAKEi495p9pX1cF/fQFFSGOYhDHVzY1FafiKg8R39lz5RWsNi2 +3SVmNLrLQ83fk84nG1d/ilD1WjPTVVL/nvdPAgMBAAEwDQYJKoZIhvcNAQEFBQAD +gYEAggKAiezCVlEf0fYvjNlQScJ/AHZTZRWWuw9+s1sGwpxEdzTpZEHKzapBSm+w +lctBERt1Dl7WNICPIqnkzs2atgyWfuYq8M7rhnor37gTOsi1nVtxnEBhVsb5kLhP +HRSIsq1ACPWIqxBddG2mKT0bAWifKUgk2T1SR8q/Iq/9e98= +-----END CERTIFICATE----- diff --git a/test/server/serfserverkey.pem b/test/server/serfserverkey.pem new file mode 100644 index 0000000..ed461a2 --- /dev/null +++ b/test/server/serfserverkey.pem @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,8412FEAB72C5064F + +7hX15avmcrhdzHcirFHRc3clpNSnln+QFAsqXZjVc0TGWSKkrpQco783LDfIzLF1 +SWQUseIsIfELrxuFRV32kEwI75pwP27X82M5sDhp2Bw3Dxv++ZeMNRoCX1a3+fkO +2I9QC6KFbvqdMeQXDAcwJXrR9ASCHtVoQbrdoMpoBo6n4NwRrF9loaPHtHgSnF64 +dQjmT0s7QU99qW/PbODjzfzpY+rqrtyu3lXx+XVkFTWrDTU+DpFXfOyUF3iqSd5U +pEW1pimvRPF5geG8xxhAeOEfb8XZJINsfs1OI/5zpPyeXmBnwVNdgEW368pZ2ePp +1MUd6Te6OwXoZXkCYghWm4iQSv2zSulCUq6pa55Rgb5+Zl8d74+vJ9N4Gt+g+0w6 +PkhkhizZiExYFV1GYmmTRx9BjzXgmo9qMDDtr9xENYB5KWgvSm1KmmUxKFZJZcPu +zkv7vvlwrCPsUlsEOySanA0h6IME+ktPaQkBBG1aa28uorZD4Nbl81XYVt7jNifC +hz9IHFxRjf51xA9WHQxDorztg74I0ZcOK1hcpCo40ivrI2Oruq3Zo/15eJNnorW2 +4cru/LaU29CZs1EaYLoANh7RFhTaQb+MkeYluwU7O6mF2DBaWvO+8lu61dLhLtEM +SkJ3O1rOH5JzUf/bLat9L9fwGNJzPYcA1/3S6r+5q3tVdgR2s47v6WBOhXFFuR7K +f5DC83PjOYSeWcgCoFP/Z1kW15pnJua4ORqo1NSvqFTh/jaeZHLDaX08f0GkR8s4 +c26JsBsKf6QWqQmISaWYQK2YgeEeAxM/tEnAUH2AouTJnu+6XzsL4w== +-----END RSA PRIVATE KEY----- diff --git a/test/server/test_server.c b/test/server/test_server.c index 1ad2c9e..6130d6e 100644 --- a/test/server/test_server.c +++ b/test/server/test_server.c @@ -23,44 +23,6 @@ #include "test_server.h" -struct serv_ctx_t { - /* Pool for resource allocation. */ - apr_pool_t *pool; - - apr_int32_t options; - - /* Array of actions which server will replay when client connected. */ - test_server_action_t *action_list; - /* Size of action_list array. */ - apr_size_t action_count; - /* Index of current action. */ - apr_size_t cur_action; - - /* Array of messages the server will receive from the client. */ - test_server_message_t *message_list; - /* Size of message_list array. */ - apr_size_t message_count; - /* Index of current message. */ - apr_size_t cur_message; - - /* Number of messages received that the server didn't respond to yet. */ - apr_size_t outstanding_responses; - - /* Position in message buffer (incoming messages being read). */ - apr_size_t message_buf_pos; - - /* Position in action buffer. (outgoing messages being sent). */ - apr_size_t action_buf_pos; - - /* Address for server binding. */ - apr_sockaddr_t *serv_addr; - apr_socket_t *serv_sock; - - /* Accepted client socket. NULL if there is no client socket. */ - apr_socket_t *client_sock; - -}; - /* Replay support functions */ static void next_message(serv_ctx_t *servctx) { @@ -73,6 +35,20 @@ static void next_action(serv_ctx_t *servctx) servctx->action_buf_pos = 0; } +static apr_status_t +socket_write(serv_ctx_t *serv_ctx, const char *data, + apr_size_t *len) +{ + return apr_socket_send(serv_ctx->client_sock, data, len); +} + +static apr_status_t +socket_read(serv_ctx_t *serv_ctx, char *data, + apr_size_t *len) +{ + return apr_socket_recv(serv_ctx->client_sock, data, len); +} + /* Verify received requests and take the necessary actions (return a response, kill the connection ...) */ static apr_status_t replay(serv_ctx_t *servctx, @@ -94,7 +70,7 @@ static apr_status_t replay(serv_ctx_t *servctx, char buf[128]; apr_size_t len = sizeof(buf); - status = apr_socket_recv(servctx->client_sock, buf, &len); + status = servctx->read(servctx, buf, &len); if (! APR_STATUS_IS_EAGAIN(status)) { /* we're out of actions! */ printf("Received more requests than expected.\n"); @@ -110,7 +86,7 @@ static apr_status_t replay(serv_ctx_t *servctx, char buf[128]; apr_size_t len = sizeof(buf); - status = apr_socket_recv(servctx->client_sock, buf, &len); + status = servctx->read(servctx, buf, &len); if (status == APR_EOF) { apr_socket_close(servctx->client_sock); @@ -135,7 +111,7 @@ static apr_status_t replay(serv_ctx_t *servctx, if (len > sizeof(buf)) len = sizeof(buf); - status = apr_socket_recv(servctx->client_sock, buf, &len); + status = servctx->read(servctx, buf, &len); if (status != APR_SUCCESS) return status; @@ -176,8 +152,9 @@ static apr_status_t replay(serv_ctx_t *servctx, msg_len = strlen(action->text); len = msg_len - servctx->action_buf_pos; - status = apr_socket_send(servctx->client_sock, - action->text + servctx->action_buf_pos, &len); + status = servctx->send(servctx, + action->text + servctx->action_buf_pos, + &len); if (status != APR_SUCCESS) return status; @@ -219,7 +196,13 @@ apr_status_t test_server_run(serv_ctx_t *servctx, const apr_pollfd_t *desc; /* create a new pollset */ +#ifdef BROKEN_WSAPOLL + status = apr_pollset_create_ex(&pollset, 32, pool, 0, + APR_POLLSET_SELECT); +#else status = apr_pollset_create(&pollset, 32, pool, 0); +#endif + if (status != APR_SUCCESS) return status; @@ -261,6 +244,13 @@ apr_status_t test_server_run(serv_ctx_t *servctx, } if (desc->desc.s == servctx->client_sock) { + if (servctx->handshake) { + status = servctx->handshake(servctx); + } + + if (status) + goto cleanup; + /* Replay data to socket. */ status = replay(servctx, desc->rtnevents, pool); @@ -286,20 +276,20 @@ cleanup: return status; } -/* Start a TCP server on port SERV_PORT in thread THREAD. srv_replay is a array - of action to replay when connection started. replay_count is count of - actions in srv_replay. */ -apr_status_t test_start_server(serv_ctx_t **servctx_p, - apr_sockaddr_t *address, - test_server_message_t *message_list, - apr_size_t message_count, - test_server_action_t *action_list, - apr_size_t action_count, - apr_int32_t options, - apr_pool_t *pool) + +/* Setup the context needed to start a TCP server on adress. + message_list is a list of expected requests. + action_list is the list of responses to be returned in order. + */ +void test_setup_server(serv_ctx_t **servctx_p, + apr_sockaddr_t *address, + test_server_message_t *message_list, + apr_size_t message_count, + test_server_action_t *action_list, + apr_size_t action_count, + apr_int32_t options, + apr_pool_t *pool) { - apr_status_t status; - apr_socket_t *serv_sock; serv_ctx_t *servctx; servctx = apr_pcalloc(pool, sizeof(*servctx)); @@ -313,12 +303,58 @@ apr_status_t test_start_server(serv_ctx_t **servctx_p, servctx->action_list = action_list; servctx->action_count = action_count; + /* Start replay from first action. */ + servctx->cur_action = 0; + servctx->action_buf_pos = 0; + servctx->outstanding_responses = 0; + + servctx->read = socket_read; + servctx->send = socket_write; + + *servctx_p = servctx; +} + +void test_setup_https_server(serv_ctx_t **servctx_p, + apr_sockaddr_t *address, + test_server_message_t *message_list, + apr_size_t message_count, + test_server_action_t *action_list, + apr_size_t action_count, + apr_int32_t options, + const char *keyfile, + const char *certfile, + apr_pool_t *pool) +{ + serv_ctx_t *servctx; + + test_setup_server(servctx_p, address, message_list, + message_count, action_list, action_count, + options, pool); + + servctx = *servctx_p; + + servctx->handshake = ssl_handshake; + /* Override with SSL encrypt/decrypt functions */ + servctx->read = ssl_socket_read; + servctx->send = ssl_socket_write; + + init_ssl_context(servctx, keyfile, certfile); +} + +apr_status_t test_start_server(serv_ctx_t *servctx) +{ + apr_status_t status; + apr_socket_t *serv_sock; + /* create server socket */ #if APR_VERSION_AT_LEAST(1, 0, 0) - status = apr_socket_create(&serv_sock, address->family, SOCK_STREAM, 0, - pool); + status = apr_socket_create(&serv_sock, servctx->serv_addr->family, + SOCK_STREAM, 0, + servctx->pool); #else - status = apr_socket_create(&serv_sock, address->family, SOCK_STREAM, pool); + status = apr_socket_create(&serv_sock, servctx->serv_addr->family, + SOCK_STREAM, + servctx->pool); #endif if (status != APR_SUCCESS) @@ -332,28 +368,29 @@ apr_status_t test_start_server(serv_ctx_t **servctx_p, if (status != APR_SUCCESS) return status; - /* Start replay from first action. */ - servctx->cur_action = 0; - servctx->action_buf_pos = 0; - servctx->outstanding_responses = 0; - /* listen for clients */ - apr_socket_listen(serv_sock, SOMAXCONN); + status = apr_socket_listen(serv_sock, SOMAXCONN); if (status != APR_SUCCESS) return status; servctx->serv_sock = serv_sock; servctx->client_sock = NULL; + return APR_SUCCESS; } apr_status_t test_server_destroy(serv_ctx_t *servctx, apr_pool_t *pool) { - apr_socket_close(servctx->serv_sock); + apr_status_t status; + + status = apr_socket_close(servctx->serv_sock); if (servctx->client_sock) { apr_socket_close(servctx->client_sock); } - return APR_SUCCESS; + if (servctx->ssl_ctx) + cleanup_ssl_context(servctx); + + return status; } diff --git a/test/server/test_server.h b/test/server/test_server.h index 9e8b0e2..65a61e6 100644 --- a/test/server/test_server.h +++ b/test/server/test_server.h @@ -16,8 +16,6 @@ #ifndef TEST_SERVER_H #define TEST_SERVER_H -typedef struct serv_ctx_t serv_ctx_t; - #define TEST_SERVER_DUMP 1 /* Default port for our test server. */ @@ -26,6 +24,15 @@ typedef struct serv_ctx_t serv_ctx_t; #define PROXY_PORT 23456 +typedef struct serv_ctx_t serv_ctx_t; + +typedef apr_status_t (*send_func_t)(serv_ctx_t *serv_ctx, const char *data, + apr_size_t *len); +typedef apr_status_t (*receive_func_t)(serv_ctx_t *serv_ctx, char *data, + apr_size_t *len); + +typedef apr_status_t (*handshake_func_t)(serv_ctx_t *serv_ctx); + typedef struct { enum { @@ -44,14 +51,70 @@ typedef struct const char *text; } test_server_message_t; -apr_status_t test_start_server(serv_ctx_t **servctx_p, - apr_sockaddr_t *address, - test_server_message_t *message_list, - apr_size_t message_count, - test_server_action_t *action_list, - apr_size_t action_count, - apr_int32_t options, - apr_pool_t *pool); +struct serv_ctx_t { + /* Pool for resource allocation. */ + apr_pool_t *pool; + + apr_int32_t options; + + /* Array of actions which server will replay when client connected. */ + test_server_action_t *action_list; + /* Size of action_list array. */ + apr_size_t action_count; + /* Index of current action. */ + apr_size_t cur_action; + + /* Array of messages the server will receive from the client. */ + test_server_message_t *message_list; + /* Size of message_list array. */ + apr_size_t message_count; + /* Index of current message. */ + apr_size_t cur_message; + + /* Number of messages received that the server didn't respond to yet. */ + apr_size_t outstanding_responses; + + /* Position in message buffer (incoming messages being read). */ + apr_size_t message_buf_pos; + + /* Position in action buffer. (outgoing messages being sent). */ + apr_size_t action_buf_pos; + + /* Address for server binding. */ + apr_sockaddr_t *serv_addr; + apr_socket_t *serv_sock; + + /* Accepted client socket. NULL if there is no client socket. */ + apr_socket_t *client_sock; + + send_func_t send; + receive_func_t read; + + handshake_func_t handshake; + void *ssl_ctx; +}; + +void test_setup_server(serv_ctx_t **servctx_p, + apr_sockaddr_t *address, + test_server_message_t *message_list, + apr_size_t message_count, + test_server_action_t *action_list, + apr_size_t action_count, + apr_int32_t options, + apr_pool_t *pool); + +void test_setup_https_server(serv_ctx_t **servctx_p, + apr_sockaddr_t *address, + test_server_message_t *message_list, + apr_size_t message_count, + test_server_action_t *action_list, + apr_size_t action_count, + apr_int32_t options, + const char *keyfile, + const char *certfile, + apr_pool_t *pool); + +apr_status_t test_start_server(serv_ctx_t *serv_ctx); apr_status_t test_server_run(serv_ctx_t *servctx, apr_short_interval_time_t duration, @@ -59,6 +122,16 @@ apr_status_t test_server_run(serv_ctx_t *servctx, apr_status_t test_server_destroy(serv_ctx_t *servctx, apr_pool_t *pool); +apr_status_t init_ssl_context(serv_ctx_t *serv_ctx, + const char *keyfile, + const char *certfile); +apr_status_t ssl_handshake(serv_ctx_t *servctx); +apr_status_t ssl_socket_write(serv_ctx_t *serv_ctx, const char *data, + apr_size_t *len); +apr_status_t ssl_socket_read(serv_ctx_t *serv_ctx, char *data, apr_size_t *len); +void cleanup_ssl_context(serv_ctx_t *serv_ctx); + + #ifndef APR_VERSION_AT_LEAST /* Introduced in APR 1.3.0 */ #define APR_VERSION_AT_LEAST(major,minor,patch) \ (((major) < APR_MAJOR_VERSION) \ @@ -67,4 +140,5 @@ apr_status_t test_server_destroy(serv_ctx_t *servctx, apr_pool_t *pool); (patch) <= APR_PATCH_VERSION)) #endif /* APR_VERSION_AT_LEAST */ + #endif /* TEST_SERVER_H */ diff --git a/test/server/test_sslserver.c b/test/server/test_sslserver.c new file mode 100644 index 0000000..74492ec --- /dev/null +++ b/test/server/test_sslserver.c @@ -0,0 +1,252 @@ +/* Copyright 2013 Justin Erenkrantz and Greg Stein + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "serf.h" +#include "test_server.h" + +#include "serf_private.h" + +//#ifdef SERF_HAVE_OPENSSL + +#include <openssl/bio.h> +#include <openssl/ssl.h> +#include <openssl/err.h> + +#define TEST_VERBOSE 0 + +static int init_done = 0; + +typedef struct ssl_context_t { + int handshake_done; + + SSL_CTX* ctx; + SSL* ssl; + BIO *bio; + +} ssl_context_t; + +int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata) +{ + strncpy(buf, "serftest", size); + buf[size - 1] = '\0'; + return strlen(buf); +} + +static int bio_apr_socket_create(BIO *bio) +{ + bio->shutdown = 1; + bio->init = 1; + bio->num = -1; + bio->ptr = NULL; + + return 1; +} + +static int bio_apr_socket_destroy(BIO *bio) +{ + /* Did we already free this? */ + if (bio == NULL) { + return 0; + } + + return 1; +} + +static long bio_apr_socket_ctrl(BIO *bio, int cmd, long num, void *ptr) +{ + long ret = 1; + + switch (cmd) { + default: + /* abort(); */ + break; + case BIO_CTRL_FLUSH: + /* At this point we can't force a flush. */ + break; + case BIO_CTRL_PUSH: + case BIO_CTRL_POP: + ret = 0; + break; + } + return ret; +} + +/* Returns the amount read. */ +static int bio_apr_socket_read(BIO *bio, char *in, int inlen) +{ + apr_size_t len = inlen; + serv_ctx_t *serv_ctx = bio->ptr; + apr_status_t status; + + BIO_clear_retry_flags(bio); + + status = apr_socket_recv(serv_ctx->client_sock, in, &len); + if (status == APR_EAGAIN) { + BIO_set_retry_read(bio); + if (len == 0) + return -1; + + } + + if (SERF_BUCKET_READ_ERROR(status)) + return -1; + + serf__log(TEST_VERBOSE, __FILE__, "Read %d bytes from socket with status %d.\n", + len, status); + + return len; +} + +/* Returns the amount written. */ +static int bio_apr_socket_write(BIO *bio, const char *in, int inlen) +{ + apr_size_t len = inlen; + serv_ctx_t *serv_ctx = bio->ptr; + + apr_status_t status = apr_socket_send(serv_ctx->client_sock, in, &len); + + if (SERF_BUCKET_READ_ERROR(status)) + return -1; + + serf__log(TEST_VERBOSE, __FILE__, "Wrote %d of %d bytes to socket.\n", + len, inlen); + + return len; +} + + +static BIO_METHOD bio_apr_socket_method = { + BIO_TYPE_SOCKET, + "APR sockets", + bio_apr_socket_write, + bio_apr_socket_read, + NULL, /* Is this called? */ + NULL, /* Is this called? */ + bio_apr_socket_ctrl, + bio_apr_socket_create, + bio_apr_socket_destroy, +#ifdef OPENSSL_VERSION_NUMBER + NULL /* sslc does not have the callback_ctrl field */ +#endif +}; + +apr_status_t init_ssl_context(serv_ctx_t *serv_ctx, + const char *keyfile, + const char *certfile) +{ + ssl_context_t *ssl_ctx = apr_pcalloc(serv_ctx->pool, sizeof(*ssl_ctx)); + serv_ctx->ssl_ctx = ssl_ctx; + + /* Init OpenSSL globally */ + if (!init_done) + { + CRYPTO_malloc_init(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + init_done = 1; + } + + /* Init this connection */ + if (!ssl_ctx->ctx) { + ssl_ctx->ctx = SSL_CTX_new(SSLv23_server_method()); + SSL_CTX_set_cipher_list(ssl_ctx->ctx, "ALL"); + SSL_CTX_set_default_passwd_cb(ssl_ctx->ctx, pem_passwd_cb); + + ssl_ctx->ssl = SSL_new(ssl_ctx->ctx); + SSL_use_PrivateKey_file(ssl_ctx->ssl, keyfile, SSL_FILETYPE_PEM); + SSL_use_certificate_file(ssl_ctx->ssl, certfile, SSL_FILETYPE_PEM); + + + ssl_ctx->bio = BIO_new(&bio_apr_socket_method); + ssl_ctx->bio->ptr = serv_ctx; + SSL_set_bio(ssl_ctx->ssl, ssl_ctx->bio, ssl_ctx->bio); + } + + return APR_SUCCESS; +} + +apr_status_t ssl_handshake(serv_ctx_t *serv_ctx) +{ + ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; + int result; + + if (ssl_ctx->handshake_done) + return APR_SUCCESS; + + /* SSL handshake */ + result = SSL_accept(ssl_ctx->ssl); + if (result == 1) { + serf__log(TEST_VERBOSE, __FILE__, "Handshake successful.\n"); + ssl_ctx->handshake_done = 1; + } + else { + int ssl_err; + + ssl_err = SSL_get_error(ssl_ctx->ssl, result); + switch (ssl_err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return APR_EAGAIN; + default: + serf__log(TEST_VERBOSE, __FILE__, "SSL Error %d: ", ssl_err); + ERR_print_errors_fp(stderr); + serf__log_nopref(TEST_VERBOSE, "\n"); + return APR_EGENERAL; + } + } + + return APR_EAGAIN; +} + +apr_status_t +ssl_socket_write(serv_ctx_t *serv_ctx, const char *data, + apr_size_t *len) +{ + ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; + + int result = SSL_write(ssl_ctx->ssl, data, *len); + if (result > 0) { + *len = result; + return APR_SUCCESS; + } + + return APR_EGENERAL; +} + +apr_status_t +ssl_socket_read(serv_ctx_t *serv_ctx, char *data, + apr_size_t *len) +{ + ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; + + int result = SSL_read(ssl_ctx->ssl, data, *len); + if (result > 0) { + *len = result; + return APR_SUCCESS; + } + + return APR_EGENERAL; +} + +void cleanup_ssl_context(serv_ctx_t *serv_ctx) +{ + ssl_context_t *ssl_ctx = serv_ctx->ssl_ctx; + + SSL_clear(ssl_ctx->ssl); + SSL_CTX_free(ssl_ctx->ctx); +} +//#endif /* SERF_HAVE_OPENSSL */
\ No newline at end of file diff --git a/test/test_buckets.c b/test/test_buckets.c index 7d4f7ac..4707d70 100644 --- a/test/test_buckets.c +++ b/test/test_buckets.c @@ -20,8 +20,44 @@ #include "serf.h" #include "test_serf.h" +/* test case has access to internal functions. */ +#include "serf_private.h" + #define CRLF "\r\n" +static apr_status_t read_all(serf_bucket_t *bkt, + char *buf, + apr_size_t buf_len, + apr_size_t *read_len) +{ + const char *data; + apr_size_t data_len; + apr_status_t status; + apr_size_t read; + + read = 0; + + do + { + status = serf_bucket_read(bkt, SERF_READ_ALL_AVAIL, &data, &data_len); + + if (!SERF_BUCKET_READ_ERROR(status)) + { + if (data_len > buf_len - read) + { + /* Buffer is not large enough to read all data */ + data_len = buf_len - read; + status = APR_EGENERAL; + } + memcpy(buf + read, data, data_len); + read += data_len; + } + } while(status == APR_SUCCESS); + + *read_len = read; + return status; +} + static void test_simple_bucket_readline(CuTest *tc) { apr_status_t status; @@ -201,7 +237,7 @@ static void test_bucket_header_set(CuTest *tc) CuAssertStrEquals(tc, "bar,baz,test", serf_bucket_headers_get(hdrs, "Foo")); - // headers are case insensitive. + /* headers are case insensitive. */ CuAssertStrEquals(tc, "bar,baz,test", serf_bucket_headers_get(hdrs, "fOo")); test_teardown(test_pool); } @@ -384,6 +420,44 @@ static void test_iovec_buckets(CuTest *tc) test_teardown(test_pool); } +/* Construct a header bucket with some headers, and then read from it. */ +static void test_header_buckets(CuTest *tc) +{ + apr_status_t status; + apr_pool_t *test_pool = test_setup(); + serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL, + NULL); + const char *cur; + + serf_bucket_t *hdrs = serf_bucket_headers_create(alloc); + CuAssertTrue(tc, hdrs != NULL); + + serf_bucket_headers_set(hdrs, "Content-Type", "text/plain"); + serf_bucket_headers_set(hdrs, "Content-Length", "100"); + + /* Note: order not guaranteed, assume here that it's fifo. */ + cur = "Content-Type: text/plain" CRLF + "Content-Length: 100" CRLF + CRLF + CRLF; + while (1) { + const char *data; + apr_size_t len; + + status = serf_bucket_read(hdrs, SERF_READ_ALL_AVAIL, &data, &len); + CuAssert(tc, "Unexpected error when waiting for response headers", + !SERF_BUCKET_READ_ERROR(status)); + if (SERF_BUCKET_READ_ERROR(status) || + APR_STATUS_IS_EOF(status)) + break; + + /* Check that the bytes read match with expected at current position. */ + CuAssertStrnEquals(tc, cur, len, data); + cur += len; + } + CuAssertIntEquals(tc, APR_EOF, status); +} + static void test_aggregate_buckets(CuTest *tc) { apr_status_t status; @@ -409,6 +483,255 @@ static void test_aggregate_buckets(CuTest *tc) test_teardown(test_pool); } +/* Test for issue: the server aborts the connection in the middle of + streaming the body of the response, where the length was set with the + Content-Length header. Test that we get a decent error code from the + response bucket instead of APR_EOF. */ +static void test_response_body_too_small_cl(CuTest *tc) +{ + serf_bucket_t *bkt, *tmp; + apr_pool_t *test_pool = test_setup(); + serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL, + NULL); + + /* Make a response of 60 bytes, but set the Content-Length to 100. */ +#define BODY "12345678901234567890"\ + "12345678901234567890"\ + "12345678901234567890" + + tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF + "Content-Type: text/plain" CRLF + "Content-Length: 100" CRLF + CRLF + BODY, + alloc); + + bkt = serf_bucket_response_create(tmp, alloc); + + { + const char *data; + apr_size_t len; + apr_status_t status; + + status = serf_bucket_read(bkt, SERF_READ_ALL_AVAIL, &data, &len); + + CuAssert(tc, "Read more data than expected.", + strlen(BODY) >= len); + CuAssert(tc, "Read data is not equal to expected.", + strncmp(BODY, data, len) == 0); + CuAssert(tc, "Error expected due to response body too short!", + SERF_BUCKET_READ_ERROR(status)); + CuAssertIntEquals(tc, SERF_ERROR_TRUNCATED_HTTP_RESPONSE, status); + } +} +#undef BODY + +/* Test for issue: the server aborts the connection in the middle of + streaming the body of the response, using chunked encoding. Test that we get + a decent error code from the response bucket instead of APR_EOF. */ +static void test_response_body_too_small_chunked(CuTest *tc) +{ + serf_bucket_t *bkt, *tmp; + apr_pool_t *test_pool = test_setup(); + serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL, + NULL); + + /* Make a response of 60 bytes, but set the chunk size to 60 and don't end + with chunk of length 0. */ +#define BODY "12345678901234567890"\ +"12345678901234567890"\ +"12345678901234567890" + + tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF + "Content-Type: text/plain" CRLF + "Transfer-Encoding: chunked" CRLF + CRLF + "64" CRLF BODY, + alloc); + + bkt = serf_bucket_response_create(tmp, alloc); + + { + const char *data; + apr_size_t len; + apr_status_t status; + + status = serf_bucket_read(bkt, SERF_READ_ALL_AVAIL, &data, &len); + + CuAssert(tc, "Read more data than expected.", + strlen(BODY) >= len); + CuAssert(tc, "Read data is not equal to expected.", + strncmp(BODY, data, len) == 0); + CuAssert(tc, "Error expected due to response body too short!", + SERF_BUCKET_READ_ERROR(status)); + CuAssertIntEquals(tc, SERF_ERROR_TRUNCATED_HTTP_RESPONSE, status); + } +} +#undef BODY + +/* Test for issue: the server aborts the connection in the middle of + streaming trailing CRLF after body chunk. Test that we get + a decent error code from the response bucket instead of APR_EOF. */ +static void test_response_body_chunked_no_crlf(CuTest *tc) +{ + serf_bucket_t *bkt, *tmp; + apr_pool_t *test_pool = test_setup(); + serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL, + NULL); + + tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF + "Content-Type: text/plain" CRLF + "Transfer-Encoding: chunked" CRLF + CRLF + "2" CRLF + "AB", + alloc); + + bkt = serf_bucket_response_create(tmp, alloc); + + { + char buf[1024]; + apr_size_t len; + apr_status_t status; + + status = read_all(bkt, buf, sizeof(buf), &len); + + CuAssertIntEquals(tc, SERF_ERROR_TRUNCATED_HTTP_RESPONSE, status); + } + test_teardown(test_pool); +} + +/* Test for issue: the server aborts the connection in the middle of + streaming trailing CRLF after body chunk. Test that we get + a decent error code from the response bucket instead of APR_EOF. */ +static void test_response_body_chunked_incomplete_crlf(CuTest *tc) +{ + serf_bucket_t *bkt, *tmp; + apr_pool_t *test_pool = test_setup(); + serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL, + NULL); + + tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF + "Content-Type: text/plain" CRLF + "Transfer-Encoding: chunked" CRLF + CRLF + "2" CRLF + "AB" + "\r", + alloc); + + bkt = serf_bucket_response_create(tmp, alloc); + + { + char buf[1024]; + apr_size_t len; + apr_status_t status; + + status = read_all(bkt, buf, sizeof(buf), &len); + + CuAssertIntEquals(tc, SERF_ERROR_TRUNCATED_HTTP_RESPONSE, status); + } + test_teardown(test_pool); +} + +static void test_response_body_chunked_gzip_small(CuTest *tc) +{ + serf_bucket_t *bkt, *tmp; + apr_pool_t *test_pool = test_setup(); + serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL, + NULL); + + tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF + "Content-Type: text/plain" CRLF + "Transfer-Encoding: chunked" CRLF + "Content-Encoding: gzip" CRLF + CRLF + "2" CRLF + "A", + alloc); + + bkt = serf_bucket_response_create(tmp, alloc); + + { + char buf[1024]; + apr_size_t len; + apr_status_t status; + + status = read_all(bkt, buf, sizeof(buf), &len); + + CuAssertIntEquals(tc, SERF_ERROR_TRUNCATED_HTTP_RESPONSE, status); + } + test_teardown(test_pool); +} + +static void test_response_bucket_peek_at_headers(CuTest *tc) +{ + apr_pool_t *test_pool = test_setup(); + serf_bucket_t *resp_bkt1, *tmp, *hdrs; + serf_status_line sl; + serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL, + NULL); + const char *hdr_val, *cur; + apr_status_t status; + +#define EXP_RESPONSE "HTTP/1.1 200 OK" CRLF\ + "Content-Type: text/plain" CRLF\ + "Content-Length: 100" CRLF\ + CRLF\ + "12345678901234567890"\ + "12345678901234567890"\ + "12345678901234567890" + + tmp = SERF_BUCKET_SIMPLE_STRING(EXP_RESPONSE, + alloc); + + resp_bkt1 = serf_bucket_response_create(tmp, alloc); + + status = serf_bucket_response_status(resp_bkt1, &sl); + CuAssertIntEquals(tc, 200, sl.code); + CuAssertStrEquals(tc, "OK", sl.reason); + CuAssertIntEquals(tc, SERF_HTTP_11, sl.version); + + /* Ensure that the status line & headers are read in the response_bucket. */ + status = serf_bucket_response_wait_for_headers(resp_bkt1); + CuAssert(tc, "Unexpected error when waiting for response headers", + !SERF_BUCKET_READ_ERROR(status)); + + hdrs = serf_bucket_response_get_headers(resp_bkt1); + CuAssertPtrNotNull(tc, hdrs); + + hdr_val = serf_bucket_headers_get(hdrs, "Content-Type"); + CuAssertStrEquals(tc, "text/plain", hdr_val); + hdr_val = serf_bucket_headers_get(hdrs, "Content-Length"); + CuAssertStrEquals(tc, "100", hdr_val); + + /* Create a new bucket for the response which still has the original + status line & headers. */ + + status = serf_response_full_become_aggregate(resp_bkt1); + CuAssertIntEquals(tc, APR_SUCCESS, status); + cur = EXP_RESPONSE; + + while (1) { + const char *data; + apr_size_t len; + apr_status_t status; + + status = serf_bucket_read(resp_bkt1, SERF_READ_ALL_AVAIL, &data, &len); + CuAssert(tc, "Unexpected error when waiting for response headers", + !SERF_BUCKET_READ_ERROR(status)); + if (SERF_BUCKET_READ_ERROR(status) || + APR_STATUS_IS_EOF(status)) + break; + + /* Check that the bytes read match with expected at current position. */ + CuAssertStrnEquals(tc, cur, len, data); + cur += len; + } + +} +#undef EXP_RESPONSE + CuSuite *test_buckets(void) { CuSuite *suite = CuSuiteNew(); @@ -417,9 +740,16 @@ CuSuite *test_buckets(void) SUITE_ADD_TEST(suite, test_response_bucket_read); SUITE_ADD_TEST(suite, test_response_bucket_headers); SUITE_ADD_TEST(suite, test_response_bucket_chunked_read); + SUITE_ADD_TEST(suite, test_response_body_too_small_cl); + SUITE_ADD_TEST(suite, test_response_body_too_small_chunked); + SUITE_ADD_TEST(suite, test_response_body_chunked_no_crlf); + SUITE_ADD_TEST(suite, test_response_body_chunked_incomplete_crlf); + SUITE_ADD_TEST(suite, test_response_body_chunked_gzip_small); + SUITE_ADD_TEST(suite, test_response_bucket_peek_at_headers); SUITE_ADD_TEST(suite, test_bucket_header_set); SUITE_ADD_TEST(suite, test_iovec_buckets); SUITE_ADD_TEST(suite, test_aggregate_buckets); + SUITE_ADD_TEST(suite, test_header_buckets); return suite; } diff --git a/test/test_context.c b/test/test_context.c index d2a24d0..14d4fa9 100644 --- a/test/test_context.c +++ b/test/test_context.c @@ -43,6 +43,61 @@ typedef struct { test_baton_t *tb; } handler_baton_t; +/* Helper function, runs the client and server context loops and validates + that no errors were encountered, and all messages were sent and received. */ +static apr_status_t +test_helper_run_requests_no_check(CuTest *tc, test_baton_t *tb, + int num_requests, + handler_baton_t handler_ctx[], + apr_pool_t *pool) +{ + apr_pool_t *iter_pool; + int i, done = 0; + apr_status_t status; + + apr_pool_create(&iter_pool, pool); + + while (!done) + { + apr_pool_clear(iter_pool); + + status = test_server_run(tb->serv_ctx, 0, iter_pool); + if (!APR_STATUS_IS_TIMEUP(status) && + SERF_BUCKET_READ_ERROR(status)) + return status; + + status = serf_context_run(tb->context, 0, iter_pool); + if (!APR_STATUS_IS_TIMEUP(status) && + SERF_BUCKET_READ_ERROR(status)) + return status; + + done = 1; + for (i = 0; i < num_requests; i++) + done &= handler_ctx[i].done; + } + apr_pool_destroy(iter_pool); + + return APR_SUCCESS; +} + +static void +test_helper_run_requests_expect_ok(CuTest *tc, test_baton_t *tb, + int num_requests, + handler_baton_t handler_ctx[], + apr_pool_t *pool) +{ + apr_status_t status; + + status = test_helper_run_requests_no_check(tc, tb, num_requests, + handler_ctx, pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + /* Check that all requests were received */ + CuAssertIntEquals(tc, num_requests, tb->sent_requests->nelts); + CuAssertIntEquals(tc, num_requests, tb->accepted_requests->nelts); + CuAssertIntEquals(tc, num_requests, tb->handled_requests->nelts); +} + static serf_bucket_t* accept_response(serf_request_t *request, serf_bucket_t *stream, void *acceptor_baton, @@ -133,15 +188,67 @@ static apr_status_t handle_response(serf_request_t *request, return APR_SUCCESS; } +static void setup_handler(test_baton_t *tb, handler_baton_t *handler_ctx, + const char *method, const char *path, + int req_id, + serf_response_handler_t handler) +{ + handler_ctx->method = method; + handler_ctx->path = path; + handler_ctx->done = FALSE; + + handler_ctx->acceptor = accept_response; + handler_ctx->acceptor_baton = NULL; + handler_ctx->handler = handler ? handler : handle_response; + handler_ctx->req_id = req_id; + handler_ctx->accepted_requests = tb->accepted_requests; + handler_ctx->sent_requests = tb->sent_requests; + handler_ctx->handled_requests = tb->handled_requests; + handler_ctx->tb = tb; +} + +static void create_new_prio_request(test_baton_t *tb, + handler_baton_t *handler_ctx, + const char *method, const char *path, + int req_id) +{ + setup_handler(tb, handler_ctx, method, path, req_id, NULL); + serf_connection_priority_request_create(tb->connection, + setup_request, + handler_ctx); +} + +static void create_new_request(test_baton_t *tb, + handler_baton_t *handler_ctx, + const char *method, const char *path, + int req_id) +{ + setup_handler(tb, handler_ctx, method, path, req_id, NULL); + serf_connection_request_create(tb->connection, + setup_request, + handler_ctx); +} + +static void +create_new_request_with_resp_hdlr(test_baton_t *tb, + handler_baton_t *handler_ctx, + const char *method, const char *path, + int req_id, + serf_response_handler_t handler) +{ + setup_handler(tb, handler_ctx, method, path, req_id, handler); + serf_connection_request_create(tb->connection, + setup_request, + handler_ctx); +} + /* Validate that requests are sent and completed in the order of creation. */ static void test_serf_connection_request_create(CuTest *tc) { test_baton_t *tb; - serf_request_t *request1, *request2; - handler_baton_t handler_ctx, handler2_ctx; + handler_baton_t handler_ctx[2]; + const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]); apr_status_t status; - apr_pool_t *iter_pool; - apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; int i; test_server_message_t message_list[] = { {CHUNKED_REQUEST(1, "1")}, @@ -152,74 +259,31 @@ static void test_serf_connection_request_create(CuTest *tc) {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, }; - apr_pool_t *test_pool = test_setup(); - accepted_requests = apr_array_make(test_pool, 2, sizeof(int)); - sent_requests = apr_array_make(test_pool, 2, sizeof(int)); - handled_requests = apr_array_make(test_pool, 2, sizeof(int)); + apr_pool_t *test_pool = test_setup(); /* Set up a test context with a server */ status = test_server_setup(&tb, - message_list, 2, - action_list, 2, 0, NULL, + message_list, num_requests, + action_list, num_requests, 0, NULL, test_pool); CuAssertIntEquals(tc, APR_SUCCESS, status); - handler_ctx.method = "GET"; - handler_ctx.path = "/"; - handler_ctx.done = FALSE; - - handler_ctx.acceptor = accept_response; - handler_ctx.acceptor_baton = NULL; - handler_ctx.handler = handle_response; - handler_ctx.req_id = 1; - handler_ctx.accepted_requests = accepted_requests; - handler_ctx.sent_requests = sent_requests; - handler_ctx.handled_requests = handled_requests; - - request1 = serf_connection_request_create(tb->connection, - setup_request, - &handler_ctx); + create_new_request(tb, &handler_ctx[0], "GET", "/", 1); + create_new_request(tb, &handler_ctx[1], "GET", "/", 2); - handler2_ctx = handler_ctx; - handler2_ctx.req_id = 2; - - request2 = serf_connection_request_create(tb->connection, - setup_request, - &handler2_ctx); - - apr_pool_create(&iter_pool, test_pool); - - while (!handler_ctx.done || !handler2_ctx.done) - { - apr_pool_clear(iter_pool); - - status = test_server_run(tb->serv_ctx, 0, iter_pool); - if (APR_STATUS_IS_TIMEUP(status)) - status = APR_SUCCESS; - CuAssertIntEquals(tc, APR_SUCCESS, status); - - status = serf_context_run(tb->context, 0, iter_pool); - if (APR_STATUS_IS_TIMEUP(status)) - status = APR_SUCCESS; - CuAssertIntEquals(tc, APR_SUCCESS, status); - } - apr_pool_destroy(iter_pool); - - /* Check that all requests were received */ - CuAssertIntEquals(tc, 2, sent_requests->nelts); - CuAssertIntEquals(tc, 2, accepted_requests->nelts); - CuAssertIntEquals(tc, 2, handled_requests->nelts); + test_helper_run_requests_expect_ok(tc, tb, num_requests, handler_ctx, + test_pool); /* Check that the requests were sent in the order we created them */ - for (i = 0; i < sent_requests->nelts; i++) { - int req_nr = APR_ARRAY_IDX(sent_requests, i, int); + for (i = 0; i < tb->sent_requests->nelts; i++) { + int req_nr = APR_ARRAY_IDX(tb->sent_requests, i, int); CuAssertIntEquals(tc, i + 1, req_nr); } /* Check that the requests were received in the order we created them */ - for (i = 0; i < handled_requests->nelts; i++) { - int req_nr = APR_ARRAY_IDX(handled_requests, i, int); + for (i = 0; i < tb->handled_requests->nelts; i++) { + int req_nr = APR_ARRAY_IDX(tb->handled_requests, i, int); CuAssertIntEquals(tc, i + 1, req_nr); } @@ -232,11 +296,9 @@ static void test_serf_connection_request_create(CuTest *tc) static void test_serf_connection_priority_request_create(CuTest *tc) { test_baton_t *tb; - serf_request_t *request1, *request2, *request3; - handler_baton_t handler_ctx, handler2_ctx, handler3_ctx; + handler_baton_t handler_ctx[3]; + const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]); apr_status_t status; - apr_pool_t *iter_pool; - apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; int i; test_server_message_t message_list[] = { @@ -253,81 +315,29 @@ static void test_serf_connection_priority_request_create(CuTest *tc) apr_pool_t *test_pool = test_setup(); - accepted_requests = apr_array_make(test_pool, 3, sizeof(int)); - sent_requests = apr_array_make(test_pool, 3, sizeof(int)); - handled_requests = apr_array_make(test_pool, 3, sizeof(int)); - /* Set up a test context with a server */ status = test_server_setup(&tb, - message_list, 3, - action_list, 3, 0, NULL, + message_list, num_requests, + action_list, num_requests, 0, NULL, test_pool); CuAssertIntEquals(tc, APR_SUCCESS, status); - handler_ctx.method = "GET"; - handler_ctx.path = "/"; - handler_ctx.done = FALSE; - - handler_ctx.acceptor = accept_response; - handler_ctx.acceptor_baton = NULL; - handler_ctx.handler = handle_response; - handler_ctx.req_id = 2; - handler_ctx.accepted_requests = accepted_requests; - handler_ctx.sent_requests = sent_requests; - handler_ctx.handled_requests = handled_requests; - - request1 = serf_connection_request_create(tb->connection, - setup_request, - &handler_ctx); - - handler2_ctx = handler_ctx; - handler2_ctx.req_id = 3; + create_new_request(tb, &handler_ctx[0], "GET", "/", 2); + create_new_request(tb, &handler_ctx[1], "GET", "/", 3); + create_new_prio_request(tb, &handler_ctx[2], "GET", "/", 1); - request2 = serf_connection_request_create(tb->connection, - setup_request, - &handler2_ctx); - handler3_ctx = handler_ctx; - handler3_ctx.req_id = 1; - - request3 = serf_connection_priority_request_create(tb->connection, - setup_request, - &handler3_ctx); - - apr_pool_create(&iter_pool, test_pool); - - while (!handler_ctx.done || !handler2_ctx.done || !handler3_ctx.done) - { - apr_pool_clear(iter_pool); - - status = test_server_run(tb->serv_ctx, 0, iter_pool); - if (APR_STATUS_IS_TIMEUP(status)) - status = APR_SUCCESS; - CuAssertIntEquals(tc, APR_SUCCESS, status); - - status = serf_context_run(tb->context, 0, iter_pool); - if (APR_STATUS_IS_TIMEUP(status)) - status = APR_SUCCESS; - CuAssertIntEquals(tc, APR_SUCCESS, status); - - /* Debugging purposes only! */ - serf_debug__closed_conn(tb->bkt_alloc); - } - apr_pool_destroy(iter_pool); - - /* Check that all requests were received */ - CuAssertIntEquals(tc, 3, sent_requests->nelts); - CuAssertIntEquals(tc, 3, accepted_requests->nelts); - CuAssertIntEquals(tc, 3, handled_requests->nelts); + test_helper_run_requests_expect_ok(tc, tb, num_requests, handler_ctx, + test_pool); /* Check that the requests were sent in the order we created them */ - for (i = 0; i < sent_requests->nelts; i++) { - int req_nr = APR_ARRAY_IDX(sent_requests, i, int); + for (i = 0; i < tb->sent_requests->nelts; i++) { + int req_nr = APR_ARRAY_IDX(tb->sent_requests, i, int); CuAssertIntEquals(tc, i + 1, req_nr); } /* Check that the requests were received in the order we created them */ - for (i = 0; i < handled_requests->nelts; i++) { - int req_nr = APR_ARRAY_IDX(handled_requests, i, int); + for (i = 0; i < tb->handled_requests->nelts; i++) { + int req_nr = APR_ARRAY_IDX(tb->handled_requests, i, int); CuAssertIntEquals(tc, i + 1, req_nr); } @@ -337,13 +347,12 @@ static void test_serf_connection_priority_request_create(CuTest *tc) /* Test that serf correctly handles the 'Connection:close' header when the server is planning to close the connection. */ -#define NUM_REQUESTS 10 static void test_serf_closed_connection(CuTest *tc) { test_baton_t *tb; - apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; apr_status_t status; - handler_baton_t handler_ctx[NUM_REQUESTS]; + handler_baton_t handler_ctx[10]; + const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]); int done = FALSE, i; test_server_message_t message_list[] = { @@ -390,37 +399,18 @@ static void test_serf_closed_connection(CuTest *tc) apr_pool_t *test_pool = test_setup(); - accepted_requests = apr_array_make(test_pool, NUM_REQUESTS, sizeof(int)); - sent_requests = apr_array_make(test_pool, NUM_REQUESTS, sizeof(int)); - handled_requests = apr_array_make(test_pool, NUM_REQUESTS, sizeof(int)); - /* Set up a test context with a server. */ status = test_server_setup(&tb, - message_list, 10, + message_list, num_requests, action_list, 12, 0, NULL, test_pool); CuAssertIntEquals(tc, APR_SUCCESS, status); - for (i = 0 ; i < NUM_REQUESTS ; i++) { - /* Send some requests on the connections */ - handler_ctx[i].method = "GET"; - handler_ctx[i].path = "/"; - handler_ctx[i].done = FALSE; - - handler_ctx[i].acceptor = accept_response; - handler_ctx[i].acceptor_baton = NULL; - handler_ctx[i].handler = handle_response; - handler_ctx[i].req_id = i+1; - handler_ctx[i].accepted_requests = accepted_requests; - handler_ctx[i].sent_requests = sent_requests; - handler_ctx[i].handled_requests = handled_requests; - handler_ctx[i].tb = tb; - - serf_connection_request_create(tb->connection, - setup_request, - &handler_ctx[i]); + /* Send some requests on the connections */ + for (i = 0 ; i < num_requests ; i++) { + create_new_request(tb, &handler_ctx[i], "GET", "/", i+1); } while (1) { @@ -438,7 +428,7 @@ static void test_serf_closed_connection(CuTest *tc) serf_debug__closed_conn(tb->bkt_alloc); done = TRUE; - for (i = 0 ; i < NUM_REQUESTS ; i++) + for (i = 0 ; i < num_requests ; i++) if (handler_ctx[i].done == FALSE) { done = FALSE; break; @@ -447,29 +437,26 @@ static void test_serf_closed_connection(CuTest *tc) break; } - /* Check that all requests were received */ - CuAssertTrue(tc, sent_requests->nelts >= NUM_REQUESTS); - CuAssertIntEquals(tc, NUM_REQUESTS, accepted_requests->nelts); - CuAssertIntEquals(tc, NUM_REQUESTS, handled_requests->nelts); + /* Check that all requests were received */ + CuAssertTrue(tc, tb->sent_requests->nelts >= num_requests); + CuAssertIntEquals(tc, num_requests, tb->accepted_requests->nelts); + CuAssertIntEquals(tc, num_requests, tb->handled_requests->nelts); /* Cleanup */ test_server_teardown(tb, test_pool); test_teardown(test_pool); } -#undef NUM_REQUESTS /* Test if serf is sending the request to the proxy, not to the server directly. */ static void test_serf_setup_proxy(CuTest *tc) { test_baton_t *tb; - serf_request_t *request; - handler_baton_t handler_ctx; - apr_status_t status; - apr_pool_t *iter_pool; - apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; int i; - int numrequests = 1; + handler_baton_t handler_ctx[1]; + const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]); + apr_pool_t *iter_pool; + apr_status_t status; test_server_message_t message_list[] = { {"GET http://localhost:" SERV_PORT_STR " HTTP/1.1" CRLF\ @@ -488,10 +475,6 @@ static void test_serf_setup_proxy(CuTest *tc) apr_pool_t *test_pool = test_setup(); - accepted_requests = apr_array_make(test_pool, numrequests, sizeof(int)); - sent_requests = apr_array_make(test_pool, numrequests, sizeof(int)); - handled_requests = apr_array_make(test_pool, numrequests, sizeof(int)); - /* Set up a test context with a server, no messages expected. */ status = test_server_proxy_setup(&tb, /* server messages and actions */ @@ -504,25 +487,11 @@ static void test_serf_setup_proxy(CuTest *tc) NULL, test_pool); CuAssertIntEquals(tc, APR_SUCCESS, status); - handler_ctx.method = "GET"; - handler_ctx.path = "/"; - handler_ctx.done = FALSE; - - handler_ctx.acceptor = accept_response; - handler_ctx.acceptor_baton = NULL; - handler_ctx.handler = handle_response; - handler_ctx.req_id = 1; - handler_ctx.accepted_requests = accepted_requests; - handler_ctx.sent_requests = sent_requests; - handler_ctx.handled_requests = handled_requests; - - request = serf_connection_request_create(tb->connection, - setup_request, - &handler_ctx); + create_new_request(tb, &handler_ctx[0], "GET", "/", 1); apr_pool_create(&iter_pool, test_pool); - while (!handler_ctx.done) + while (!handler_ctx[0].done) { apr_pool_clear(iter_pool); @@ -547,19 +516,20 @@ static void test_serf_setup_proxy(CuTest *tc) apr_pool_destroy(iter_pool); /* Check that all requests were received */ - CuAssertIntEquals(tc, numrequests, sent_requests->nelts); - CuAssertIntEquals(tc, numrequests, accepted_requests->nelts); - CuAssertIntEquals(tc, numrequests, handled_requests->nelts); + CuAssertIntEquals(tc, num_requests, tb->sent_requests->nelts); + CuAssertIntEquals(tc, num_requests, tb->accepted_requests->nelts); + CuAssertIntEquals(tc, num_requests, tb->handled_requests->nelts); + /* Check that the requests were sent in the order we created them */ - for (i = 0; i < sent_requests->nelts; i++) { - int req_nr = APR_ARRAY_IDX(sent_requests, i, int); + for (i = 0; i < tb->sent_requests->nelts; i++) { + int req_nr = APR_ARRAY_IDX(tb->sent_requests, i, int); CuAssertIntEquals(tc, i + 1, req_nr); } /* Check that the requests were received in the order we created them */ - for (i = 0; i < handled_requests->nelts; i++) { - int req_nr = APR_ARRAY_IDX(handled_requests, i, int); + for (i = 0; i < tb->handled_requests->nelts; i++) { + int req_nr = APR_ARRAY_IDX(tb->handled_requests, i, int); CuAssertIntEquals(tc, i + 1, req_nr); } @@ -617,7 +587,6 @@ handle_response_keepalive_limit(serf_request_t *request, static void test_keepalive_limit_one_by_one(CuTest *tc) { test_baton_t *tb; - apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; apr_status_t status; handler_baton_t handler_ctx[SEND_REQUESTS]; int done = FALSE, i; @@ -644,35 +613,17 @@ static void test_keepalive_limit_one_by_one(CuTest *tc) apr_pool_t *test_pool = test_setup(); - accepted_requests = apr_array_make(test_pool, RCVD_REQUESTS, sizeof(int)); - sent_requests = apr_array_make(test_pool, RCVD_REQUESTS, sizeof(int)); - handled_requests = apr_array_make(test_pool, RCVD_REQUESTS, sizeof(int)); - /* Set up a test context with a server. */ status = test_server_setup(&tb, - message_list, 7, - action_list, 7, 0, NULL, + message_list, RCVD_REQUESTS, + action_list, RCVD_REQUESTS, 0, NULL, test_pool); CuAssertIntEquals(tc, APR_SUCCESS, status); for (i = 0 ; i < SEND_REQUESTS ; i++) { - /* Send some requests on the connections */ - handler_ctx[i].method = "GET"; - handler_ctx[i].path = "/"; - handler_ctx[i].done = FALSE; - - handler_ctx[i].acceptor = accept_response; - handler_ctx[i].acceptor_baton = NULL; - handler_ctx[i].handler = handle_response_keepalive_limit; - handler_ctx[i].req_id = i+1; - handler_ctx[i].accepted_requests = accepted_requests; - handler_ctx[i].sent_requests = sent_requests; - handler_ctx[i].handled_requests = handled_requests; - handler_ctx[i].tb = tb; - - serf_connection_request_create(tb->connection, - setup_request, - &handler_ctx[i]); + create_new_request_with_resp_hdlr(tb, &handler_ctx[i], "GET", "/", i+1, + handle_response_keepalive_limit); + /* TODO: don't think this needs to be done in the loop. */ serf_connection_set_max_outstanding_requests(tb->connection, 1); } @@ -701,9 +652,9 @@ static void test_keepalive_limit_one_by_one(CuTest *tc) } /* Check that all requests were received */ - CuAssertIntEquals(tc, RCVD_REQUESTS, sent_requests->nelts); - CuAssertIntEquals(tc, RCVD_REQUESTS, accepted_requests->nelts); - CuAssertIntEquals(tc, RCVD_REQUESTS, handled_requests->nelts); + CuAssertIntEquals(tc, RCVD_REQUESTS, tb->sent_requests->nelts); + CuAssertIntEquals(tc, RCVD_REQUESTS, tb->accepted_requests->nelts); + CuAssertIntEquals(tc, RCVD_REQUESTS, tb->handled_requests->nelts); /* Cleanup */ test_server_teardown(tb, test_pool); @@ -771,8 +722,7 @@ handle_response_keepalive_limit_burst(serf_request_t *request, static void test_keepalive_limit_one_by_one_and_burst(CuTest *tc) { test_baton_t *tb; - apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; - apr_status_t status; + apr_status_t status; handler_baton_t handler_ctx[SEND_REQUESTS]; int done = FALSE, i; @@ -798,35 +748,16 @@ static void test_keepalive_limit_one_by_one_and_burst(CuTest *tc) apr_pool_t *test_pool = test_setup(); - accepted_requests = apr_array_make(test_pool, RCVD_REQUESTS, sizeof(int)); - sent_requests = apr_array_make(test_pool, RCVD_REQUESTS, sizeof(int)); - handled_requests = apr_array_make(test_pool, RCVD_REQUESTS, sizeof(int)); - /* Set up a test context with a server. */ status = test_server_setup(&tb, - message_list, 7, - action_list, 7, 0, NULL, + message_list, RCVD_REQUESTS, + action_list, RCVD_REQUESTS, 0, NULL, test_pool); CuAssertIntEquals(tc, APR_SUCCESS, status); for (i = 0 ; i < SEND_REQUESTS ; i++) { - /* Send some requests on the connections */ - handler_ctx[i].method = "GET"; - handler_ctx[i].path = "/"; - handler_ctx[i].done = FALSE; - - handler_ctx[i].acceptor = accept_response; - handler_ctx[i].acceptor_baton = NULL; - handler_ctx[i].handler = handle_response_keepalive_limit_burst; - handler_ctx[i].req_id = i+1; - handler_ctx[i].accepted_requests = accepted_requests; - handler_ctx[i].sent_requests = sent_requests; - handler_ctx[i].handled_requests = handled_requests; - handler_ctx[i].tb = tb; - - serf_connection_request_create(tb->connection, - setup_request, - &handler_ctx[i]); + create_new_request_with_resp_hdlr(tb, &handler_ctx[i], "GET", "/", i+1, + handle_response_keepalive_limit_burst); serf_connection_set_max_outstanding_requests(tb->connection, 1); } @@ -855,9 +786,9 @@ static void test_keepalive_limit_one_by_one_and_burst(CuTest *tc) } /* Check that all requests were received */ - CuAssertIntEquals(tc, RCVD_REQUESTS, sent_requests->nelts); - CuAssertIntEquals(tc, RCVD_REQUESTS, accepted_requests->nelts); - CuAssertIntEquals(tc, RCVD_REQUESTS, handled_requests->nelts); + CuAssertIntEquals(tc, RCVD_REQUESTS, tb->sent_requests->nelts); + CuAssertIntEquals(tc, RCVD_REQUESTS, tb->accepted_requests->nelts); + CuAssertIntEquals(tc, RCVD_REQUESTS, tb->handled_requests->nelts); /* Cleanup */ test_server_teardown(tb, test_pool); @@ -866,7 +797,6 @@ static void test_keepalive_limit_one_by_one_and_burst(CuTest *tc) #undef SEND_REQUESTS #undef RCVD_REQUESTS -#define NUM_REQUESTS 5 typedef struct { apr_off_t read; apr_off_t written; @@ -896,10 +826,10 @@ static apr_status_t progress_conn_setup(apr_socket_t *skt, static void test_serf_progress_callback(CuTest *tc) { test_baton_t *tb; - apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; apr_status_t status; - handler_baton_t handler_ctx[NUM_REQUESTS]; - int done = FALSE, i; + handler_baton_t handler_ctx[5]; + const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]); + int i; progress_baton_t *pb; test_server_message_t message_list[] = { @@ -919,15 +849,11 @@ static void test_serf_progress_callback(CuTest *tc) }; apr_pool_t *test_pool = test_setup(); - - accepted_requests = apr_array_make(test_pool, NUM_REQUESTS, sizeof(int)); - sent_requests = apr_array_make(test_pool, NUM_REQUESTS, sizeof(int)); - handled_requests = apr_array_make(test_pool, NUM_REQUESTS, sizeof(int)); - + /* Set up a test context with a server. */ status = test_server_setup(&tb, - message_list, 5, - action_list, 5, 0, + message_list, num_requests, + action_list, num_requests, 0, progress_conn_setup, test_pool); CuAssertIntEquals(tc, APR_SUCCESS, status); @@ -936,64 +862,384 @@ static void test_serf_progress_callback(CuTest *tc) tb->user_baton = pb; serf_context_set_progress_cb(tb->context, progress_cb, tb); - for (i = 0 ; i < NUM_REQUESTS ; i++) { - /* Send some requests on the connections */ - handler_ctx[i].method = "GET"; - handler_ctx[i].path = "/"; - handler_ctx[i].done = FALSE; - - handler_ctx[i].acceptor = accept_response; - handler_ctx[i].acceptor_baton = NULL; - handler_ctx[i].handler = handle_response; - handler_ctx[i].req_id = i+1; - handler_ctx[i].accepted_requests = accepted_requests; - handler_ctx[i].sent_requests = sent_requests; - handler_ctx[i].handled_requests = handled_requests; - handler_ctx[i].tb = tb; - - serf_connection_request_create(tb->connection, + /* Send some requests on the connections */ + for (i = 0 ; i < num_requests ; i++) { + create_new_request(tb, &handler_ctx[i], "GET", "/", i+1); + } + + test_helper_run_requests_expect_ok(tc, tb, num_requests, handler_ctx, + test_pool); + + /* Check that progress was reported. */ + CuAssertTrue(tc, pb->written > 0); + CuAssertTrue(tc, pb->read > 0); + + /* Cleanup */ + test_server_teardown(tb, test_pool); + test_teardown(test_pool); +} + + +/***************************************************************************** + * Issue #91: test that serf correctly handle an incoming 4xx reponse while + * the outgoing request wasn't written completely yet. + *****************************************************************************/ + +#define REQUEST_PART1 "PROPFIND / HTTP/1.1" CRLF\ +"Host: lgo-ubuntu.local" CRLF\ +"User-Agent: SVN/1.8.0-dev (x86_64-apple-darwin11.4.2) serf/2.0.0" CRLF\ +"Content-Type: text/xml" CRLF\ +"Transfer-Encoding: chunked" CRLF \ +CRLF\ +"12d" CRLF\ +"<?xml version=""1.0"" encoding=""utf-8""?><propfind xmlns=""DAV:""><prop>" + +#define REQUEST_PART2 \ +"<resourcetype xmlns=""DAV:""/><getcontentlength xmlns=""DAV:""/>"\ +"<deadprop-count xmlns=""http://subversion.tigris.org/xmlns/dav/""/>"\ +"<version-name xmlns=""DAV:""/><creationdate xmlns=""DAV:""/>"\ +"<creator-displayname xmlns=""DAV:""/></prop></propfind>" CRLF\ +"0" CRLF \ +CRLF + +#define RESPONSE_408 "HTTP/1.1 408 Request Time-out" CRLF\ +"Date: Wed, 14 Nov 2012 19:50:35 GMT" CRLF\ +"Server: Apache/2.2.17 (Ubuntu)" CRLF\ +"Vary: Accept-Encoding" CRLF\ +"Content-Length: 305" CRLF\ +"Connection: close" CRLF\ +"Content-Type: text/html; charset=iso-8859-1" CRLF \ +CRLF\ +"<!DOCTYPE HTML PUBLIC ""-//IETF//DTD HTML 2.0//EN""><html><head>"\ +"<title>408 Request Time-out</title></head><body><h1>Request Time-out</h1>"\ +"<p>Server timeout waiting for the HTTP request from the client.</p><hr>"\ +"<address>Apache/2.2.17 (Ubuntu) Server at lgo-ubuntu.local Port 80</address>"\ +"</body></html>" + + +static apr_status_t detect_eof(void *baton, serf_bucket_t *aggregate_bucket) +{ + serf_bucket_t *body_bkt; + handler_baton_t *ctx = baton; + + if (ctx->done) { + body_bkt = serf_bucket_simple_create(REQUEST_PART1, strlen(REQUEST_PART2), + NULL, NULL, + ctx->tb->bkt_alloc); + serf_bucket_aggregate_append(aggregate_bucket, body_bkt); + } + + return APR_EAGAIN; +} + +static apr_status_t setup_request_timeout( + serf_request_t *request, + void *setup_baton, + serf_bucket_t **req_bkt, + serf_response_acceptor_t *acceptor, + void **acceptor_baton, + serf_response_handler_t *handler, + void **handler_baton, + apr_pool_t *pool) +{ + handler_baton_t *ctx = setup_baton; + serf_bucket_t *body_bkt; + + *req_bkt = serf__bucket_stream_create(serf_request_get_alloc(request), + detect_eof, + ctx); + + /* create a simple body text */ + body_bkt = serf_bucket_simple_create(REQUEST_PART1, strlen(REQUEST_PART1), + NULL, NULL, + serf_request_get_alloc(request)); + serf_bucket_aggregate_append(*req_bkt, body_bkt); + + APR_ARRAY_PUSH(ctx->sent_requests, int) = ctx->req_id; + + *acceptor = ctx->acceptor; + *acceptor_baton = ctx; + *handler = ctx->handler; + *handler_baton = ctx; + + return APR_SUCCESS; +} + +static apr_status_t handle_response_timeout( + serf_request_t *request, + serf_bucket_t *response, + void *handler_baton, + apr_pool_t *pool) +{ + handler_baton_t *ctx = handler_baton; + serf_status_line sl; + apr_status_t status; + + if (! response) { + serf_connection_request_create(ctx->tb->connection, setup_request, - &handler_ctx[i]); + ctx); + return APR_SUCCESS; } - while (1) { - status = test_server_run(tb->serv_ctx, 0, test_pool); - if (APR_STATUS_IS_TIMEUP(status)) - status = APR_SUCCESS; - CuAssertIntEquals(tc, APR_SUCCESS, status); + if (serf_request_is_written(request) != APR_EBUSY) { + return APR_EGENERAL; + } - status = serf_context_run(tb->context, 0, test_pool); - if (APR_STATUS_IS_TIMEUP(status)) - status = APR_SUCCESS; - CuAssertIntEquals(tc, APR_SUCCESS, status); - /* Debugging purposes only! */ - serf_debug__closed_conn(tb->bkt_alloc); + status = serf_bucket_response_status(response, &sl); + if (SERF_BUCKET_READ_ERROR(status)) { + return status; + } + if (!sl.version && (APR_STATUS_IS_EOF(status) || + APR_STATUS_IS_EAGAIN(status))) { + return status; + } + if (sl.code == 408) { + APR_ARRAY_PUSH(ctx->handled_requests, int) = ctx->req_id; + ctx->done = TRUE; + } - done = TRUE; - for (i = 0 ; i < NUM_REQUESTS ; i++) - if (handler_ctx[i].done == FALSE) { - done = FALSE; - break; - } - if (done) - break; + /* discard the rest of the body */ + while (1) { + const char *data; + apr_size_t len; + + status = serf_bucket_read(response, 2048, &data, &len); + if (SERF_BUCKET_READ_ERROR(status) || + APR_STATUS_IS_EAGAIN(status) || + APR_STATUS_IS_EOF(status)) + return status; } - /* Check that all requests were received */ - CuAssertTrue(tc, sent_requests->nelts >= NUM_REQUESTS); - CuAssertIntEquals(tc, NUM_REQUESTS, accepted_requests->nelts); - CuAssertIntEquals(tc, NUM_REQUESTS, handled_requests->nelts); + return APR_SUCCESS; +} - /* Check that progress was reported. */ - CuAssertTrue(tc, pb->written > 0); - CuAssertTrue(tc, pb->read > 0); +static void test_serf_request_timeout(CuTest *tc) +{ + test_baton_t *tb; + apr_status_t status; + handler_baton_t handler_ctx[1]; + const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]); + + test_server_message_t message_list[] = { + {REQUEST_PART1}, + {REQUEST_PART2}, + }; + + test_server_action_t action_list[] = { + {SERVER_RESPOND, RESPONSE_408}, + }; + + apr_pool_t *test_pool = test_setup(); + + /* Set up a test context with a server. */ + status = test_server_setup(&tb, + message_list, 2, + action_list, 1, 0, + NULL, test_pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + /* Send some requests on the connection */ + handler_ctx[0].method = "PROPFIND"; + handler_ctx[0].path = "/"; + handler_ctx[0].done = FALSE; + + handler_ctx[0].acceptor = accept_response; + handler_ctx[0].acceptor_baton = NULL; + handler_ctx[0].handler = handle_response_timeout; + handler_ctx[0].req_id = 1; + handler_ctx[0].accepted_requests = tb->accepted_requests; + handler_ctx[0].sent_requests = tb->sent_requests; + handler_ctx[0].handled_requests = tb->handled_requests; + handler_ctx[0].tb = tb; + + serf_connection_request_create(tb->connection, + setup_request_timeout, + &handler_ctx[0]); + + test_helper_run_requests_expect_ok(tc, tb, num_requests, handler_ctx, + test_pool); /* Cleanup */ test_server_teardown(tb, test_pool); test_teardown(test_pool); } -#undef NUM_REQUESTS + +static apr_status_t +ssl_server_cert_cb_expect_failures(void *baton, int failures, + const serf_ssl_certificate_t *cert) +{ + /* We expect an error from the certificate validation function. */ + if (failures) + return APR_SUCCESS; + else + return APR_EGENERAL; +} + +static apr_status_t +ssl_server_cert_cb_expect_allok(void *baton, int failures, + const serf_ssl_certificate_t *cert) +{ + /* No error expected, certificate is valid. */ + if (failures) + return APR_EGENERAL; + else + return APR_SUCCESS; +} + +/* Validate that we can connect successfully to an https server. */ +static void test_serf_ssl_handshake(CuTest *tc) +{ + test_baton_t *tb; + handler_baton_t handler_ctx[1]; + const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]); + apr_status_t status; + test_server_message_t message_list[] = { + {CHUNKED_REQUEST(1, "1")}, + }; + + test_server_action_t action_list[] = { + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + }; + + + /* Set up a test context with a server */ + apr_pool_t *test_pool = test_setup(); + status = test_https_server_setup(&tb, + message_list, num_requests, + action_list, num_requests, 0, + NULL, /* default conn setup */ + "test/server/serfserverkey.pem", + "test/server/serfservercert.pem", + ssl_server_cert_cb_expect_failures, + test_pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + create_new_request(tb, &handler_ctx[0], "GET", "/", 1); + + test_helper_run_requests_expect_ok(tc, tb, num_requests, handler_ctx, + test_pool); + + test_server_teardown(tb, test_pool); + test_teardown(test_pool); +} + +static apr_status_t +https_set_root_ca_conn_setup(apr_socket_t *skt, + serf_bucket_t **input_bkt, + serf_bucket_t **output_bkt, + void *setup_baton, + apr_pool_t *pool) +{ + serf_ssl_certificate_t *cacert, *rootcacert; + test_baton_t *tb = setup_baton; + apr_status_t status; + + status = default_https_conn_setup(skt, input_bkt, output_bkt, + setup_baton, pool); + if (status) + return status; + + status = serf_ssl_load_cert_file(&cacert, "test/server/serfcacert.pem", + pool); + if (status) + return status; + status = serf_ssl_trust_cert(tb->ssl_context, cacert); + if (status) + return status; + + status = serf_ssl_load_cert_file(&rootcacert, + "test/server/serfrootcacert.pem", + pool); + if (status) + return status; + status = serf_ssl_trust_cert(tb->ssl_context, rootcacert); + if (status) + return status; + + return status; +} + +/* Validate that server certificate validation is ok when we + explicitly trust our self-signed root ca. */ +static void test_serf_ssl_trust_rootca(CuTest *tc) +{ + test_baton_t *tb; + handler_baton_t handler_ctx[1]; + const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]); + apr_status_t status; + test_server_message_t message_list[] = { + {CHUNKED_REQUEST(1, "1")}, + }; + + test_server_action_t action_list[] = { + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + }; + + /* Set up a test context with a server */ + apr_pool_t *test_pool = test_setup(); + status = test_https_server_setup(&tb, + message_list, num_requests, + action_list, num_requests, 0, + https_set_root_ca_conn_setup, + "test/server/serfserverkey.pem", + "test/server/serfservercert.pem", + ssl_server_cert_cb_expect_allok, + test_pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + create_new_request(tb, &handler_ctx[0], "GET", "/", 1); + + test_helper_run_requests_expect_ok(tc, tb, num_requests, handler_ctx, + test_pool); + + test_server_teardown(tb, test_pool); + test_teardown(test_pool); +} + +/* Validate that when the application rejects the cert, the context loop + bails out with an error. */ +static void test_serf_ssl_application_rejects_cert(CuTest *tc) +{ + test_baton_t *tb; + handler_baton_t handler_ctx[1]; + const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]); + apr_status_t status; + test_server_message_t message_list[] = { + {CHUNKED_REQUEST(1, "1")}, + }; + + test_server_action_t action_list[] = { + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + }; + + /* Set up a test context with a server */ + apr_pool_t *test_pool = test_setup(); + + /* The certificate is valid, but we tell serf to reject it by using the + ssl_server_cert_cb_expect_failures callback. */ + status = test_https_server_setup(&tb, + message_list, num_requests, + action_list, num_requests, 0, + https_set_root_ca_conn_setup, + "test/server/serfserverkey.pem", + "test/server/serfservercert.pem", + ssl_server_cert_cb_expect_failures, + test_pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + create_new_request(tb, &handler_ctx[0], "GET", "/", 1); + + status = test_helper_run_requests_no_check(tc, tb, num_requests, + handler_ctx, test_pool); + /* We expect an error from the certificate validation function. */ + CuAssert(tc, "Application told serf the certificate should be rejected," + " expected error!", status != APR_SUCCESS); + + test_server_teardown(tb, test_pool); + test_teardown(test_pool); +} CuSuite *test_context(void) { @@ -1006,6 +1252,10 @@ CuSuite *test_context(void) SUITE_ADD_TEST(suite, test_keepalive_limit_one_by_one); SUITE_ADD_TEST(suite, test_keepalive_limit_one_by_one_and_burst); SUITE_ADD_TEST(suite, test_serf_progress_callback); + SUITE_ADD_TEST(suite, test_serf_request_timeout); + SUITE_ADD_TEST(suite, test_serf_ssl_handshake); + SUITE_ADD_TEST(suite, test_serf_ssl_trust_rootca); + SUITE_ADD_TEST(suite, test_serf_ssl_application_rejects_cert); return suite; } diff --git a/test/test_serf.h b/test/test_serf.h index 454dfcc..6c0f5e7 100644 --- a/test/test_serf.h +++ b/test/test_serf.h @@ -93,8 +93,30 @@ typedef struct { /* An extra baton which can be freely used by tests. */ void *user_baton; + apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; + + serf_ssl_context_t *ssl_context; + serf_ssl_need_server_cert_t server_cert_cb; } test_baton_t; +apr_status_t default_https_conn_setup(apr_socket_t *skt, + serf_bucket_t **input_bkt, + serf_bucket_t **output_bkt, + void *setup_baton, + apr_pool_t *pool); + +apr_status_t test_https_server_setup(test_baton_t **tb_p, + test_server_message_t *message_list, + apr_size_t message_count, + test_server_action_t *action_list, + apr_size_t action_count, + apr_int32_t options, + serf_connection_setup_t conn_setup, + const char *keyfile, + const char *certfile, + serf_ssl_need_server_cert_t server_cert_cb, + apr_pool_t *pool); + apr_status_t test_server_setup(test_baton_t **tb_p, test_server_message_t *message_list, apr_size_t message_count, diff --git a/test/test_ssl.c b/test/test_ssl.c index 81b939d..43e9f9a 100644 --- a/test/test_ssl.c +++ b/test/test_ssl.c @@ -55,14 +55,28 @@ static void test_ssl_init(CuTest *tc) test_teardown(test_pool); } + +static const char * get_ca_file(apr_pool_t *pool, const char * file) +{ + char *srcdir = ""; + + if (apr_env_get(&srcdir, "srcdir", pool) == APR_SUCCESS) { + return apr_pstrcat(pool, srcdir, "/", file, NULL); + } + else { + return file; + } +} + + /* Test that loading a custom CA certificate file works. */ static void test_ssl_load_cert_file(CuTest *tc) { serf_ssl_certificate_t *cert = NULL; apr_pool_t *test_pool = test_setup(); - apr_status_t status = serf_ssl_load_cert_file(&cert, "test/serftestca.pem", - test_pool); + apr_status_t status = serf_ssl_load_cert_file( + &cert, get_ca_file(test_pool, "test/serftestca.pem"), test_pool); CuAssertIntEquals(tc, APR_SUCCESS, status); CuAssertPtrNotNull(tc, cert); @@ -78,7 +92,8 @@ static void test_ssl_cert_subject(CuTest *tc) apr_pool_t *test_pool = test_setup(); - status = serf_ssl_load_cert_file(&cert, "test/serftestca.pem", test_pool); + status = serf_ssl_load_cert_file( + &cert, get_ca_file(test_pool, "test/serftestca.pem"), test_pool); CuAssertIntEquals(tc, APR_SUCCESS, status); CuAssertPtrNotNull(tc, cert); diff --git a/test/test_util.c b/test/test_util.c index 9496195..e99fb79 100644 --- a/test/test_util.c +++ b/test/test_util.c @@ -27,7 +27,8 @@ /* Server setup function(s) */ -#define SERV_URL "http://localhost:" SERV_PORT_STR +#define HTTP_SERV_URL "http://localhost:" SERV_PORT_STR +#define HTTPS_SERV_URL "https://localhost:" SERV_PORT_STR static apr_status_t default_server_address(apr_sockaddr_t **address, apr_pool_t *pool) @@ -57,22 +58,51 @@ static void default_closed_connection(serf_connection_t *conn, } /* Default implementation of a serf_connection_setup_t callback. */ -static apr_status_t default_conn_setup(apr_socket_t *skt, - serf_bucket_t **input_bkt, - serf_bucket_t **output_bkt, - void *setup_baton, - apr_pool_t *pool) +static apr_status_t default_http_conn_setup(apr_socket_t *skt, + serf_bucket_t **input_bkt, + serf_bucket_t **output_bkt, + void *setup_baton, + apr_pool_t *pool) { - test_baton_t *ctx = setup_baton; + test_baton_t *tb = setup_baton; - *input_bkt = serf_bucket_socket_create(skt, ctx->bkt_alloc); + *input_bkt = serf_bucket_socket_create(skt, tb->bkt_alloc); return APR_SUCCESS; } +/* This function makes serf use SSL on the connection. */ +apr_status_t default_https_conn_setup(apr_socket_t *skt, + serf_bucket_t **input_bkt, + serf_bucket_t **output_bkt, + void *setup_baton, + apr_pool_t *pool) +{ + test_baton_t *tb = setup_baton; + + *input_bkt = serf_bucket_socket_create(skt, tb->bkt_alloc); + *input_bkt = serf_bucket_ssl_decrypt_create(*input_bkt, NULL, + tb->bkt_alloc); + tb->ssl_context = serf_bucket_ssl_encrypt_context_get(*input_bkt); + + if (output_bkt) { + *output_bkt = serf_bucket_ssl_encrypt_create(*output_bkt, + tb->ssl_context, + tb->bkt_alloc); + } + + if (tb->server_cert_cb) + serf_ssl_server_cert_callback_set(tb->ssl_context, + tb->server_cert_cb, + tb); + + return APR_SUCCESS; +} static apr_status_t setup(test_baton_t **tb_p, serf_connection_setup_t conn_setup, + const char *serv_url, int use_proxy, + apr_size_t message_count, apr_pool_t *pool) { apr_status_t status; @@ -86,6 +116,11 @@ static apr_status_t setup(test_baton_t **tb_p, tb->context = serf_context_create(pool); tb->bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL); + tb->accepted_requests = apr_array_make(pool, message_count, sizeof(int)); + tb->sent_requests = apr_array_make(pool, message_count, sizeof(int)); + tb->handled_requests = apr_array_make(pool, message_count, sizeof(int)); + + status = default_server_address(&tb->serv_addr, pool); if (status != APR_SUCCESS) return status; @@ -99,14 +134,13 @@ static apr_status_t setup(test_baton_t **tb_p, serf_config_proxy(tb->context, tb->proxy_addr); } - status = apr_uri_parse(pool, SERV_URL, &url); + status = apr_uri_parse(pool, serv_url, &url); if (status != APR_SUCCESS) return status; status = serf_connection_create2(&tb->connection, tb->context, url, - conn_setup ? conn_setup : - default_conn_setup, + conn_setup, tb, default_closed_connection, tb, @@ -116,6 +150,43 @@ static apr_status_t setup(test_baton_t **tb_p, } +apr_status_t test_https_server_setup(test_baton_t **tb_p, + test_server_message_t *message_list, + apr_size_t message_count, + test_server_action_t *action_list, + apr_size_t action_count, + apr_int32_t options, + serf_connection_setup_t conn_setup, + const char *keyfile, + const char *certfile, + serf_ssl_need_server_cert_t server_cert_cb, + apr_pool_t *pool) +{ + apr_status_t status; + test_baton_t *tb; + + status = setup(tb_p, + conn_setup ? conn_setup : default_https_conn_setup, + HTTPS_SERV_URL, + FALSE, + message_count, + pool); + if (status != APR_SUCCESS) + return status; + + tb = *tb_p; + tb->server_cert_cb = server_cert_cb; + + /* Prepare a server. */ + test_setup_https_server(&tb->serv_ctx, tb->serv_addr, + message_list, message_count, + action_list, action_count, options, + keyfile, certfile, + pool); + status = test_start_server(tb->serv_ctx); + + return status; +} apr_status_t test_server_setup(test_baton_t **tb_p, test_server_message_t *message_list, @@ -130,8 +201,10 @@ apr_status_t test_server_setup(test_baton_t **tb_p, test_baton_t *tb; status = setup(tb_p, - conn_setup, + conn_setup ? conn_setup : default_http_conn_setup, + HTTP_SERV_URL, FALSE, + message_count, pool); if (status != APR_SUCCESS) return status; @@ -139,9 +212,11 @@ apr_status_t test_server_setup(test_baton_t **tb_p, tb = *tb_p; /* Prepare a server. */ - status = test_start_server(&tb->serv_ctx, tb->serv_addr, - message_list, message_count, - action_list, action_count, options, pool); + test_setup_server(&tb->serv_ctx, tb->serv_addr, + message_list, message_count, + action_list, action_count, options, + pool); + status = test_start_server(tb->serv_ctx); return status; } @@ -164,8 +239,10 @@ test_server_proxy_setup(test_baton_t **tb_p, test_baton_t *tb; status = setup(tb_p, - conn_setup, + conn_setup ? conn_setup : default_http_conn_setup, + HTTP_SERV_URL, TRUE, + serv_message_count, pool); if (status != APR_SUCCESS) return status; @@ -173,18 +250,22 @@ test_server_proxy_setup(test_baton_t **tb_p, tb = *tb_p; /* Prepare the server. */ - status = test_start_server(&tb->serv_ctx, tb->serv_addr, - serv_message_list, serv_message_count, - serv_action_list, serv_action_count, - options, pool); + test_setup_server(&tb->serv_ctx, tb->serv_addr, + serv_message_list, serv_message_count, + serv_action_list, serv_action_count, + options, + pool); + status = test_start_server(tb->serv_ctx); if (status != APR_SUCCESS) return status; /* Prepare the proxy. */ - status = test_start_server(&tb->proxy_ctx, tb->proxy_addr, - proxy_message_list, proxy_message_count, - proxy_action_list, proxy_action_count, - options, pool); + test_setup_server(&tb->proxy_ctx, tb->proxy_addr, + proxy_message_list, proxy_message_count, + proxy_action_list, proxy_action_count, + options, + pool); + status = test_start_server(tb->proxy_ctx); return status; } |