From 23163abfdca791de8ce5579af36ca8d3b5a3bb0f Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Mon, 11 Jun 2012 01:14:26 +0900
Subject: [PATCH 1/2] Allow HTTP server to return text file content as reply.

Currently, the server reply must be hard-coded in test data file
and no variable substitution is performed.  This changes extends
request URI parser in sws to specify the file name which contains
text and server returns this text as a reply.

The URL and file name must be separated single '!' character. In
this case, the number following the last slash is ignored but is
still needed. The file name cannot include '/' and must be
relative to the log directory. The file should be created in
<file> section.  For example, the URL
http://%HOSTIP:%HTTPPORT/1!test.txt will make the HTTP server
return the content of the file denoted by log/test.txt.

This patch also adds Metalink to feature list.
---
 tests/FILEFORMAT   |   14 ++++++++
 tests/runtests.pl  |   10 +++++
 tests/server/sws.c |   94 +++++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 106 insertions(+), 12 deletions(-)

diff --git a/tests/FILEFORMAT b/tests/FILEFORMAT
index 5432b43..4795d85 100644
--- a/tests/FILEFORMAT
+++ b/tests/FILEFORMAT
@@ -19,6 +19,10 @@ requests curl sends, the client section defines how the client should behave
 while the verify section defines how to verify that the data stored after a
 command has been run ended up correctly.
 
+For the HTTP server, it can return the file described in <file>
+section in <client> section instead of the data described in reply
+section. See <command> section in <client> for details.
+
  Each main section has a number of available subsections that can be
 specified, that will be checked/used if specified. This document includes all
 the subsections currently supported.
@@ -186,6 +190,7 @@ socks
 unittest
 debug
 TLS-SRP
+Metalink
 
 as well as each protocol that curl supports.  A protocol only needs to be
 specified if it is different from the server (useful when the server
@@ -241,6 +246,15 @@ that is returned. The last slash in the URL must be followed by a number. That
 number (N) will be used by the test-server to load test case N and return the
 data that is defined within the <reply><data></data></reply> section.
 
+For the URL scheme sent to the HTTP server, a file name which includes
+reply of the server can be specified in the URL. The URL and file name
+must be separated single '!' character. In this case, the number
+following the last slash is ignored but is still needed. The file name
+cannot include '/' and must be relative to the log directory. The file
+should be created in <file> section.  For example, the URL
+http://%HOSTIP:%HTTPPORT/1!test.txt will make the HTTP server return
+the content of the file denoted by log/test.txt.
+
 If a CONNECT is used to the server (to emulate HTTPS etc over proxy), the port
 number given in the CONNECT request will be used to identify which test that
 is being run, if the proxy host name is said to start with 'test'.
diff --git a/tests/runtests.pl b/tests/runtests.pl
index ed234be..a90d0ec 100755
--- a/tests/runtests.pl
+++ b/tests/runtests.pl
@@ -211,6 +211,7 @@ my $has_ntlm;    # set if libcurl is built with NTLM support
 my $has_ntlm_wb; # set if libcurl is built with NTLM delegation to winbind
 my $has_charconv;# set if libcurl is built with CharConv support
 my $has_tls_srp; # set if libcurl is built with TLS-SRP support
+my $has_metalink;# set if curl is build with libmetalink
 
 my $has_openssl; # built with a lib using an OpenSSL-like API
 my $has_gnutls;  # built with GnuTLS
@@ -2339,6 +2340,10 @@ sub checksystem {
                 # TLS-SRP enabled
                 $has_tls_srp=1;
             }
+            if($feat =~ /Metalink/i) {
+                # Metalink enabled
+                $has_metalink=1;
+            }
         }
         #
         # Test harness currently uses a non-stunnel server in order to
@@ -2749,6 +2754,11 @@ sub singletest {
                 next;
             }
         }
+        elsif($f eq "Metalink") {
+            if($has_metalink) {
+                next;
+            }
+        }
         elsif($f eq "socks") {
             next;
         }
diff --git a/tests/server/sws.c b/tests/server/sws.c
index 14369e1..4d0ce90 100644
--- a/tests/server/sws.c
+++ b/tests/server/sws.c
@@ -81,6 +81,7 @@ static bool is_proxy = FALSE;
 
 #define REQBUFSIZ 150000
 #define REQBUFSIZ_TXT "149999"
+#define MAXINPUTFILESIZ 1024
 
 static long prevtestno=-1;    /* previous test number we served */
 static long prevpartno=-1;    /* previous part number we served */
@@ -118,6 +119,7 @@ struct httprequest {
   bool pipelining;   /* true if request is pipelined */
   int callcount;  /* times ProcessRequest() gets called */
   unsigned short connect_port; /* the port number CONNECT used */
+  char inputfile[MAXINPUTFILESIZ]; /* the input file containing reply */
 };
 
 static int ProcessRequest(struct httprequest *req);
@@ -321,6 +323,7 @@ static int ProcessRequest(struct httprequest *req)
   end = strstr(line, end_of_headers);
 
   req->callcount++;
+  req->inputfile[0] = '\0';
 
   logmsg("Process %d bytes request%s", req->offset,
          req->callcount > 1?" [CONTINUED]":"");
@@ -390,6 +393,11 @@ static int ProcessRequest(struct httprequest *req)
       else
         req->partno = 0;
 
+      if(*ptr == '!' && *(ptr+1)) {
+        /* Get input file path, which is the string following '!' */
+        snprintf(req->inputfile, MAXINPUTFILESIZ, "./log/%s", ptr+1);
+      }
+
       sprintf(logbuf, "Requested test number %ld part %ld",
               req->testno, req->partno);
       logmsg("%s", logbuf);
@@ -888,6 +896,46 @@ static int get_request(curl_socket_t sock, struct httprequest *req)
   return fail; /* return 0 on success */
 }
 
+/* Read content of stream. This function first checks the length of
+   the file and allocates enough memory to store the content including
+   null byte ('\0') in *out_ptr. The content in *out_ptr will be
+   null-terminated string. The length of the file + 1 is stored in
+   *outlen_ptr. */
+static int read_text_file(char **out_ptr, size_t *outlen_ptr,
+                          FILE *stream)
+{
+  int rv;
+  long pos;
+  size_t nread;
+  *out_ptr = NULL;
+  rv = fseek(stream, 0, SEEK_END);
+  if(rv == -1) {
+    goto read_text_fail;
+  }
+  pos = ftell(stream);
+  if(pos == -1) {
+    goto read_text_fail;
+  }
+  *outlen_ptr = pos;
+  rv = fseek(stream, 0, SEEK_SET);
+  if(rv == -1) {
+    goto read_text_fail;
+  }
+  *out_ptr = malloc((*outlen_ptr)+1);
+  if(*out_ptr == NULL) {
+    goto read_text_fail;
+  }
+  nread = fread(*out_ptr, 1, *outlen_ptr, stream);
+  if(nread != *outlen_ptr) {
+    goto read_text_fail;
+  }
+  (*out_ptr)[*outlen_ptr] = '\0';
+  return 0;
+ read_text_fail:
+  free(*out_ptr);
+  return -1;
+}
+
 /* returns -1 on failure */
 static int send_doc(curl_socket_t sock, struct httprequest *req)
 {
@@ -982,22 +1030,44 @@ static int send_doc(curl_socket_t sock, struct httprequest *req)
     if(0 != req->partno)
       sprintf(partbuf, "data%ld", req->partno);
 
-    stream=fopen(filename, "rb");
-    if(!stream) {
-      error = ERRNO;
-      logmsg("fopen() failed with error: %d %s", error, strerror(error));
-      logmsg("Error opening file: %s", filename);
-      logmsg("Couldn't open test file");
-      return 0;
+    if(req->inputfile[0]) {
+      stream=fopen(req->inputfile, "rb");
+      if(!stream) {
+        error = ERRNO;
+        logmsg("fopen() failed with error: %d %s", error, strerror(error));
+        logmsg("Error opening file: %s", req->inputfile);
+        logmsg("Couldn't open input file");
+        return 0;
+      }
+      else {
+        if(-1 == read_text_file(&ptr, &count, stream)) {
+          error = ERRNO;
+          logmsg("fopen() failed with error: %d %s", error, strerror(error));
+          logmsg("Error opening file: %s", req->inputfile);
+          logmsg("Couldn't read input file");
+          return 0;
+        }
+        buffer = ptr;
+      }
     }
     else {
-      error = getpart(&ptr, &count, "reply", partbuf, stream);
-      fclose(stream);
-      if(error) {
-        logmsg("getpart() failed with error: %d", error);
+      stream=fopen(filename, "rb");
+      if(!stream) {
+        error = ERRNO;
+        logmsg("fopen() failed with error: %d %s", error, strerror(error));
+        logmsg("Error opening file: %s", filename);
+        logmsg("Couldn't open test file");
         return 0;
       }
-      buffer = ptr;
+      else {
+        error = getpart(&ptr, &count, "reply", partbuf, stream);
+        fclose(stream);
+        if(error) {
+          logmsg("getpart() failed with error: %d", error);
+          return 0;
+        }
+        buffer = ptr;
+      }
     }
 
     if(got_exit_signal) {
-- 
1.7.9.1

