1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-25 01:28:51 -05:00

rustls: switch read_tls and write_tls to callbacks

And update to 0.6.0, including a rename from session to connection for
many fields.

Closes #7071
This commit is contained in:
Jacob Hoffman-Andrews 2021-05-14 18:45:49 -07:00 committed by Daniel Stenberg
parent 98770344b2
commit a62e6435f4
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
6 changed files with 159 additions and 137 deletions

View File

@ -125,7 +125,7 @@ jobs:
- libbrotli-dev - libbrotli-dev
- libzstd-dev - libzstd-dev
- env: - env:
- T=debug-rustls RUSTLS_VERSION="v0.5.0" C="--with-rustls=$HOME/crust" - T=debug-rustls RUSTLS_VERSION="v0.6.0" C="--with-rustls=$HOME/crust"
addons: addons:
apt: apt:
<<: *common_apt <<: *common_apt

View File

@ -3,7 +3,7 @@
[Rustls is a TLS backend written in Rust.](https://docs.rs/rustls/). Curl can [Rustls is a TLS backend written in Rust.](https://docs.rs/rustls/). Curl can
be built to use it as an alternative to OpenSSL or other TLS backends. We use be built to use it as an alternative to OpenSSL or other TLS backends. We use
the [crustls C bindings](https://github.com/abetterinternet/crustls/). This the [crustls C bindings](https://github.com/abetterinternet/crustls/). This
version of curl depends on version v0.5.0 of crustls. version of curl depends on version v0.6.0 of crustls.
# Building with rustls # Building with rustls
@ -12,7 +12,7 @@ First, [install Rust](https://rustup.rs/).
Next, check out, build, and install the appropriate version of crustls: Next, check out, build, and install the appropriate version of crustls:
% cargo install cbindgen % cargo install cbindgen
% git clone https://github.com/abetterinternet/crustls/ -b v0.5.0 % git clone https://github.com/abetterinternet/crustls/ -b v0.6.0
% cd crustls % cd crustls
% make % make
% make DESTDIR=${HOME}/crustls-built/ install % make DESTDIR=${HOME}/crustls-built/ install

View File

@ -37,16 +37,11 @@
#include "multiif.h" #include "multiif.h"
/* Per https://www.bearssl.org/api1.html, max TLS record size plus max
per-record overhead. */
#define TLSBUF_SIZE (16384 + 325)
struct ssl_backend_data struct ssl_backend_data
{ {
const struct rustls_client_config *config; const struct rustls_client_config *config;
struct rustls_client_session *session; struct rustls_connection *conn;
bool data_pending; bool data_pending;
uint8_t *tlsbuf;
}; };
/* For a given rustls_result error code, return the best-matching CURLcode. */ /* For a given rustls_result error code, return the best-matching CURLcode. */
@ -82,6 +77,28 @@ cr_connect(struct Curl_easy *data UNUSED_PARAM,
return CURLE_SSL_CONNECT_ERROR; return CURLE_SSL_CONNECT_ERROR;
} }
static int
read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
{
ssize_t n = sread(*(int *)userdata, buf, len);
if(n < 0) {
return SOCKERRNO;
}
*out_n = n;
return 0;
}
static int
write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
{
ssize_t n = swrite(*(int *)userdata, buf, len);
if(n < 0) {
return SOCKERRNO;
}
*out_n = n;
return 0;
}
/* /*
* On each run: * On each run:
* - Read a chunk of bytes from the socket into rustls' TLS input buffer. * - Read a chunk of bytes from the socket into rustls' TLS input buffer.
@ -101,68 +118,44 @@ cr_recv(struct Curl_easy *data, int sockindex,
struct connectdata *conn = data->conn; struct connectdata *conn = data->conn;
struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; struct ssl_connect_data *const connssl = &conn->ssl[sockindex];
struct ssl_backend_data *const backend = connssl->backend; struct ssl_backend_data *const backend = connssl->backend;
struct rustls_client_session *const session = backend->session; struct rustls_connection *const rconn = backend->conn;
curl_socket_t sockfd = conn->sock[sockindex];
size_t n = 0; size_t n = 0;
ssize_t tls_bytes_read = 0; size_t tls_bytes_read = 0;
size_t tls_bytes_processed = 0;
size_t plain_bytes_copied = 0; size_t plain_bytes_copied = 0;
rustls_result rresult = 0; rustls_result rresult = 0;
char errorbuf[255]; char errorbuf[255];
rustls_io_result io_error;
tls_bytes_read = sread(sockfd, backend->tlsbuf, TLSBUF_SIZE); io_error = rustls_connection_read_tls(rconn, read_cb,
if(tls_bytes_read == 0) { &conn->sock[sockindex], &tls_bytes_read);
if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
infof(data, "sread: EAGAIN or EWOULDBLOCK\n");
}
else if(io_error) {
failf(data, "reading from socket: %s", strerror(io_error));
*err = CURLE_READ_ERROR;
return -1;
}
else if(tls_bytes_read == 0) {
failf(data, "connection closed without TLS close_notify alert"); failf(data, "connection closed without TLS close_notify alert");
*err = CURLE_READ_ERROR; *err = CURLE_READ_ERROR;
return -1; return -1;
} }
else if(tls_bytes_read < 0) {
if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { infof(data, "cr_recv read %ld bytes from the network\n", tls_bytes_read);
infof(data, "sread: EAGAIN or EWOULDBLOCK\n");
/* There is no data in the socket right now, but there could still be rresult = rustls_connection_process_new_packets(rconn);
some data in the rustls session, so we need to read from it below. */ if(rresult != RUSTLS_RESULT_OK) {
tls_bytes_read = 0; rustls_error(rresult, errorbuf, sizeof(errorbuf), &n);
} failf(data, "%.*s", n, errorbuf);
else { *err = map_error(rresult);
failf(data, "reading from socket: %s", strerror(SOCKERRNO)); return -1;
*err = CURLE_READ_ERROR;
return -1;
}
} }
/* backend->data_pending = TRUE;
* Now pull those bytes from the buffer into ClientSession.
*/
DEBUGASSERT(tls_bytes_read >= 0);
while(tls_bytes_processed < (size_t)tls_bytes_read) {
rresult = rustls_client_session_read_tls(session,
backend->tlsbuf + tls_bytes_processed,
tls_bytes_read - tls_bytes_processed,
&n);
if(rresult != RUSTLS_RESULT_OK) {
failf(data, "error in rustls_client_session_read_tls");
*err = CURLE_READ_ERROR;
return -1;
}
else if(n == 0) {
infof(data, "EOF from rustls_client_session_read_tls\n");
break;
}
rresult = rustls_client_session_process_new_packets(session);
if(rresult != RUSTLS_RESULT_OK) {
rustls_error(rresult, errorbuf, sizeof(errorbuf), &n);
failf(data, "%.*s", n, errorbuf);
*err = map_error(rresult);
return -1;
}
tls_bytes_processed += n;
backend->data_pending = TRUE;
}
while(plain_bytes_copied < plainlen) { while(plain_bytes_copied < plainlen) {
rresult = rustls_client_session_read(session, rresult = rustls_connection_read(rconn,
(uint8_t *)plainbuf + plain_bytes_copied, (uint8_t *)plainbuf + plain_bytes_copied,
plainlen - plain_bytes_copied, plainlen - plain_bytes_copied,
&n); &n);
@ -171,20 +164,21 @@ cr_recv(struct Curl_easy *data, int sockindex,
return 0; return 0;
} }
else if(rresult != RUSTLS_RESULT_OK) { else if(rresult != RUSTLS_RESULT_OK) {
failf(data, "error in rustls_client_session_read"); failf(data, "error in rustls_connection_read");
*err = CURLE_READ_ERROR; *err = CURLE_READ_ERROR;
return -1; return -1;
} }
else if(n == 0) { else if(n == 0) {
/* rustls returns 0 from client_session_read to mean "all currently /* rustls returns 0 from connection_read to mean "all currently
available data has been read." If we bring in more ciphertext with available data has been read." If we bring in more ciphertext with
read_tls, more plaintext will become available. So don't tell curl read_tls, more plaintext will become available. So don't tell curl
this is an EOF. Instead, say "come back later." */ this is an EOF. Instead, say "come back later." */
infof(data, "EOF from rustls_client_session_read\n"); infof(data, "cr_recv got 0 bytes of plaintext\n");
backend->data_pending = FALSE; backend->data_pending = FALSE;
break; break;
} }
else { else {
infof(data, "cr_recv copied out %ld bytes of plaintext\n", n);
plain_bytes_copied += n; plain_bytes_copied += n;
} }
} }
@ -217,68 +211,50 @@ cr_send(struct Curl_easy *data, int sockindex,
struct connectdata *conn = data->conn; struct connectdata *conn = data->conn;
struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; struct ssl_connect_data *const connssl = &conn->ssl[sockindex];
struct ssl_backend_data *const backend = connssl->backend; struct ssl_backend_data *const backend = connssl->backend;
struct rustls_client_session *const session = backend->session; struct rustls_connection *const rconn = backend->conn;
curl_socket_t sockfd = conn->sock[sockindex];
ssize_t n = 0;
size_t plainwritten = 0; size_t plainwritten = 0;
size_t tlslen = 0;
size_t tlswritten = 0; size_t tlswritten = 0;
size_t tlswritten_total = 0;
rustls_result rresult; rustls_result rresult;
rustls_io_result io_error;
infof(data, "cr_send %ld bytes of plaintext\n", plainlen);
if(plainlen > 0) { if(plainlen > 0) {
rresult = rustls_client_session_write(session, rresult = rustls_connection_write(rconn, plainbuf, plainlen,
plainbuf, plainlen, &plainwritten); &plainwritten);
if(rresult != RUSTLS_RESULT_OK) { if(rresult != RUSTLS_RESULT_OK) {
failf(data, "error in rustls_client_session_write"); failf(data, "error in rustls_connection_write");
*err = CURLE_WRITE_ERROR; *err = CURLE_WRITE_ERROR;
return -1; return -1;
} }
else if(plainwritten == 0) { else if(plainwritten == 0) {
failf(data, "EOF in rustls_client_session_write"); failf(data, "EOF in rustls_connection_write");
*err = CURLE_WRITE_ERROR; *err = CURLE_WRITE_ERROR;
return -1; return -1;
} }
} }
while(rustls_client_session_wants_write(session)) { while(rustls_connection_wants_write(rconn)) {
rresult = rustls_client_session_write_tls( io_error = rustls_connection_write_tls(rconn, write_cb,
session, backend->tlsbuf, TLSBUF_SIZE, &tlslen); &conn->sock[sockindex], &tlswritten);
if(rresult != RUSTLS_RESULT_OK) { if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
failf(data, "error in rustls_client_session_write_tls"); infof(data, "swrite: EAGAIN after %ld bytes\n", tlswritten_total);
*err = CURLE_AGAIN;
return -1;
}
else if(io_error) {
failf(data, "writing to socket: %s", strerror(io_error));
*err = CURLE_WRITE_ERROR; *err = CURLE_WRITE_ERROR;
return -1; return -1;
} }
else if(tlslen == 0) { if(tlswritten == 0) {
failf(data, "EOF in rustls_client_session_write_tls"); failf(data, "EOF in swrite");
*err = CURLE_WRITE_ERROR; *err = CURLE_WRITE_ERROR;
return -1; return -1;
} }
infof(data, "cr_send wrote %ld bytes to network\n", tlswritten);
tlswritten = 0; tlswritten_total += tlswritten;
while(tlswritten < tlslen) {
n = swrite(sockfd, backend->tlsbuf + tlswritten, tlslen - tlswritten);
if(n < 0) {
if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
/* Since recv is called from poll, there should be room to
write at least some bytes before hitting EAGAIN. */
infof(data, "swrite: EAGAIN after %ld bytes\n", tlswritten);
DEBUGASSERT(tlswritten > 0);
break;
}
failf(data, "error in swrite");
*err = CURLE_WRITE_ERROR;
return -1;
}
if(n == 0) {
failf(data, "EOF in swrite");
*err = CURLE_WRITE_ERROR;
return -1;
}
tlswritten += n;
}
DEBUGASSERT(tlswritten <= tlslen);
} }
return plainwritten; return plainwritten;
@ -313,7 +289,7 @@ static CURLcode
cr_init_backend(struct Curl_easy *data, struct connectdata *conn, cr_init_backend(struct Curl_easy *data, struct connectdata *conn,
struct ssl_backend_data *const backend) struct ssl_backend_data *const backend)
{ {
struct rustls_client_session *session = backend->session; struct rustls_connection *rconn = backend->conn;
struct rustls_client_config_builder *config_builder = NULL; struct rustls_client_config_builder *config_builder = NULL;
const char *const ssl_cafile = SSL_CONN_CONFIG(CAfile); const char *const ssl_cafile = SSL_CONN_CONFIG(CAfile);
const bool verifypeer = SSL_CONN_CONFIG(verifypeer); const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
@ -326,11 +302,6 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn,
{ (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH }, { (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH },
}; };
backend->tlsbuf = calloc(TLSBUF_SIZE, 1);
if(!backend->tlsbuf) {
return CURLE_OUT_OF_MEMORY;
}
config_builder = rustls_client_config_builder_new(); config_builder = rustls_client_config_builder_new();
#ifdef USE_HTTP2 #ifdef USE_HTTP2
infof(data, "offering ALPN for HTTP/1.1 and HTTP/2\n"); infof(data, "offering ALPN for HTTP/1.1 and HTTP/2\n");
@ -341,10 +312,11 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn,
#endif #endif
if(!verifypeer) { if(!verifypeer) {
rustls_client_config_builder_dangerous_set_certificate_verifier( rustls_client_config_builder_dangerous_set_certificate_verifier(
config_builder, cr_verify_none, NULL); config_builder, cr_verify_none);
/* rustls doesn't support IP addresses (as of 0.19.0), and will reject /* rustls doesn't support IP addresses (as of 0.19.0), and will reject
* sessions created with an IP address, even when certificate verification * connections created with an IP address, even when certificate
* is turned off. Set a placeholder hostname and disable SNI. */ * verification is turned off. Set a placeholder hostname and disable
* SNI. */
if(cr_hostname_is_ip(hostname)) { if(cr_hostname_is_ip(hostname)) {
rustls_client_config_builder_set_enable_sni(config_builder, false); rustls_client_config_builder_set_enable_sni(config_builder, false);
hostname = "example.invalid"; hostname = "example.invalid";
@ -371,26 +343,26 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn,
} }
backend->config = rustls_client_config_builder_build(config_builder); backend->config = rustls_client_config_builder_build(config_builder);
DEBUGASSERT(session == NULL); DEBUGASSERT(rconn == NULL);
result = rustls_client_session_new( result = rustls_client_connection_new(backend->config, hostname, &rconn);
backend->config, hostname, &session);
if(result != RUSTLS_RESULT_OK) { if(result != RUSTLS_RESULT_OK) {
rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen); rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
failf(data, "failed to create client session: %.*s", errorlen, errorbuf); failf(data, "rustls_client_connection_new: %.*s", errorlen, errorbuf);
return CURLE_COULDNT_CONNECT; return CURLE_COULDNT_CONNECT;
} }
backend->session = session; rustls_connection_set_userdata(rconn, backend);
backend->conn = rconn;
return CURLE_OK; return CURLE_OK;
} }
static void static void
cr_set_negotiated_alpn(struct Curl_easy *data, struct connectdata *conn, cr_set_negotiated_alpn(struct Curl_easy *data, struct connectdata *conn,
const struct rustls_client_session *session) const struct rustls_connection *rconn)
{ {
const uint8_t *protocol = NULL; const uint8_t *protocol = NULL;
size_t len = 0; size_t len = 0;
rustls_client_session_get_alpn_protocol(session, &protocol, &len); rustls_connection_get_alpn_protocol(rconn, &protocol, &len);
if(NULL == protocol) { if(NULL == protocol) {
infof(data, "ALPN, server did not agree to a protocol\n"); infof(data, "ALPN, server did not agree to a protocol\n");
return; return;
@ -423,7 +395,7 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; struct ssl_connect_data *const connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex]; curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_backend_data *const backend = connssl->backend; struct ssl_backend_data *const backend = connssl->backend;
struct rustls_client_session *session = NULL; struct rustls_connection *rconn = NULL;
CURLcode tmperr = CURLE_OK; CURLcode tmperr = CURLE_OK;
int result; int result;
int what; int what;
@ -440,7 +412,7 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
connssl->state = ssl_connection_negotiating; connssl->state = ssl_connection_negotiating;
} }
session = backend->session; rconn = backend->conn;
/* Read/write data until the handshake is done or the socket would block. */ /* Read/write data until the handshake is done or the socket would block. */
for(;;) { for(;;) {
@ -451,12 +423,12 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
* then becomes true when we first write data, then becomes false again * then becomes true when we first write data, then becomes false again
* once the handshake is done. * once the handshake is done.
*/ */
if(!rustls_client_session_is_handshaking(session)) { if(!rustls_connection_is_handshaking(rconn)) {
infof(data, "Done handshaking\n"); infof(data, "Done handshaking\n");
/* Done with the handshake. Set up callbacks to send/receive data. */ /* Done with the handshake. Set up callbacks to send/receive data. */
connssl->state = ssl_connection_complete; connssl->state = ssl_connection_complete;
cr_set_negotiated_alpn(data, conn, session); cr_set_negotiated_alpn(data, conn, rconn);
conn->recv[sockindex] = cr_recv; conn->recv[sockindex] = cr_recv;
conn->send[sockindex] = cr_send; conn->send[sockindex] = cr_send;
@ -464,8 +436,8 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
return CURLE_OK; return CURLE_OK;
} }
wants_read = rustls_client_session_wants_read(session); wants_read = rustls_connection_wants_read(rconn);
wants_write = rustls_client_session_wants_write(session); wants_write = rustls_connection_wants_write(rconn);
DEBUGASSERT(wants_read || wants_write); DEBUGASSERT(wants_read || wants_write);
writefd = wants_write?sockfd:CURL_SOCKET_BAD; writefd = wants_write?sockfd:CURL_SOCKET_BAD;
readfd = wants_read?sockfd:CURL_SOCKET_BAD; readfd = wants_read?sockfd:CURL_SOCKET_BAD;
@ -489,7 +461,7 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
/* socket is readable or writable */ /* socket is readable or writable */
if(wants_write) { if(wants_write) {
infof(data, "ClientSession wants us to write_tls.\n"); infof(data, "rustls_connection wants us to write_tls.\n");
cr_send(data, sockindex, NULL, 0, &tmperr); cr_send(data, sockindex, NULL, 0, &tmperr);
if(tmperr == CURLE_AGAIN) { if(tmperr == CURLE_AGAIN) {
infof(data, "writing would block\n"); infof(data, "writing would block\n");
@ -501,7 +473,7 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
} }
if(wants_read) { if(wants_read) {
infof(data, "ClientSession wants us to read_tls.\n"); infof(data, "rustls_connection wants us to read_tls.\n");
cr_recv(data, sockindex, NULL, 0, &tmperr); cr_recv(data, sockindex, NULL, 0, &tmperr);
if(tmperr == CURLE_AGAIN) { if(tmperr == CURLE_AGAIN) {
@ -532,13 +504,13 @@ cr_getsock(struct connectdata *conn, curl_socket_t *socks)
struct ssl_connect_data *const connssl = &conn->ssl[FIRSTSOCKET]; struct ssl_connect_data *const connssl = &conn->ssl[FIRSTSOCKET];
curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
struct ssl_backend_data *const backend = connssl->backend; struct ssl_backend_data *const backend = connssl->backend;
struct rustls_client_session *session = backend->session; struct rustls_connection *rconn = backend->conn;
if(rustls_client_session_wants_write(session)) { if(rustls_connection_wants_write(rconn)) {
socks[0] = sockfd; socks[0] = sockfd;
return GETSOCK_WRITESOCK(0); return GETSOCK_WRITESOCK(0);
} }
if(rustls_client_session_wants_read(session)) { if(rustls_connection_wants_read(rconn)) {
socks[0] = sockfd; socks[0] = sockfd;
return GETSOCK_READSOCK(0); return GETSOCK_READSOCK(0);
} }
@ -551,7 +523,7 @@ cr_get_internals(struct ssl_connect_data *connssl,
CURLINFO info UNUSED_PARAM) CURLINFO info UNUSED_PARAM)
{ {
struct ssl_backend_data *backend = connssl->backend; struct ssl_backend_data *backend = connssl->backend;
return &backend->session; return &backend->conn;
} }
static void static void
@ -563,21 +535,20 @@ cr_close(struct Curl_easy *data, struct connectdata *conn,
CURLcode tmperr = CURLE_OK; CURLcode tmperr = CURLE_OK;
ssize_t n = 0; ssize_t n = 0;
if(backend->session) { if(backend->conn) {
rustls_client_session_send_close_notify(backend->session); rustls_connection_send_close_notify(backend->conn);
n = cr_send(data, sockindex, NULL, 0, &tmperr); n = cr_send(data, sockindex, NULL, 0, &tmperr);
if(n < 0) { if(n < 0) {
failf(data, "error sending close notify: %d", tmperr); failf(data, "error sending close notify: %d", tmperr);
} }
rustls_client_session_free(backend->session); rustls_connection_free(backend->conn);
backend->session = NULL; backend->conn = NULL;
} }
if(backend->config) { if(backend->config) {
rustls_client_config_free(backend->config); rustls_client_config_free(backend->config);
backend->config = NULL; backend->config = NULL;
} }
free(backend->tlsbuf);
} }
const struct Curl_ssl Curl_ssl_rustls = { const struct Curl_ssl Curl_ssl_rustls = {

View File

@ -70,7 +70,7 @@ if test "x$OPT_RUSTLS" != xno; then
CPPFLAGS="$CPPFLAGS $addcflags" CPPFLAGS="$CPPFLAGS $addcflags"
fi fi
AC_CHECK_LIB(crustls, rustls_client_session_read, AC_CHECK_LIB(crustls, rustls_connection_read,
[ [
AC_DEFINE(USE_RUSTLS, 1, [if rustls is enabled]) AC_DEFINE(USE_RUSTLS, 1, [if rustls is enabled])
AC_SUBST(USE_RUSTLS, [1]) AC_SUBST(USE_RUSTLS, [1])

View File

@ -60,7 +60,7 @@ test325 test326 test327 test328 test329 test330 test331 test332 test333 \
test334 test335 test336 test337 test338 test339 test340 test341 test342 \ test334 test335 test336 test337 test338 test339 test340 test341 test342 \
test343 test344 test345 test346 test347 test348 test349 test350 test351 \ test343 test344 test345 test346 test347 test348 test349 test350 test351 \
test352 test353 test354 test355 test356 test357 test358 test359 test360 \ test352 test353 test354 test355 test356 test357 test358 test359 test360 \
test361 test362 test363 \ test361 test362 test363 test364 \
\ \
test393 test394 test395 test396 test397 \ test393 test394 test395 test396 test397 \
\ \

51
tests/data/test364 Normal file
View File

@ -0,0 +1,51 @@
<testcase>
<info>
<keywords>
HTTPS
HTTPS PUT
</keywords>
</info>
# Server-side
<reply>
<data>
HTTP/1.0 200 OK swsclose
Date: Tue, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake
blablabla
</data>
</reply>
# Client-side
<client>
<features>
SSL
</features>
<server>
https
</server>
<name>
HTTPS PUT of small file
</name>
<command>
-k https://%HOSTIP:%HTTPSPORT/we/want/%TESTNUMBER -T log/test%TESTNUMBER.txt
</command>
<file name="log/test%TESTNUMBER.txt">
%repeat[200 x banana]%
</file>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol>
PUT /we/want/%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPSPORT
User-Agent: curl/%VERSION
Accept: */*
Content-Length: 1201
Expect: 100-continue
%repeat[200 x banana]%
</protocol>
</verify>
</testcase>