diff options
Diffstat (limited to 'qa/mockup')
-rw-r--r-- | qa/mockup/Makefile.am | 15 | ||||
-rw-r--r-- | qa/mockup/curl-mockup.cxx | 504 | ||||
-rw-r--r-- | qa/mockup/curl/curl.h | 153 | ||||
-rw-r--r-- | qa/mockup/internals.hxx | 133 | ||||
-rw-r--r-- | qa/mockup/mockup-config.cxx | 409 | ||||
-rw-r--r-- | qa/mockup/mockup-config.h | 113 |
6 files changed, 1327 insertions, 0 deletions
diff --git a/qa/mockup/Makefile.am b/qa/mockup/Makefile.am new file mode 100644 index 0000000..ef34e12 --- /dev/null +++ b/qa/mockup/Makefile.am @@ -0,0 +1,15 @@ +if !OS_WIN32 +noinst_LTLIBRARIES = libcmis-mockup.la + +libcmis_mockup_la_SOURCES = \ + curl-mockup.cxx \ + internals.hxx \ + mockup-config.cxx \ + mockup-config.h \ + curl/curl.h + +libcmis_mockup_la_LIBADD = \ + $(top_builddir)/src/libcmis/libcmis.la + +libcmis_mockup_la_LDFLAGS = -export-dynamic -no-undefined +endif diff --git a/qa/mockup/curl-mockup.cxx b/qa/mockup/curl-mockup.cxx new file mode 100644 index 0000000..0a74a64 --- /dev/null +++ b/qa/mockup/curl-mockup.cxx @@ -0,0 +1,504 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sstream> + +#include "curl/curl.h" +#include "internals.hxx" + +using namespace std; + +namespace mockup +{ + extern Configuration* config; +} + +/** Code mostly coming from curl + */ +struct curl_slist *curl_slist_append( struct curl_slist * list, const char * data ) +{ + struct curl_slist* new_item = ( struct curl_slist* ) malloc( sizeof( struct curl_slist ) ); + + if( new_item ) + { + char *dupdata = strdup(data); + if ( dupdata ) + { + new_item->next = NULL; + new_item->data = dupdata; + } + else + { + free(new_item); + return NULL; + } + } + else + return NULL; + + if ( list ) + { + curl_slist* last = list; + while ( last->next ) + last = last->next; + last->next = new_item; + return list; + } + + /* if this is the first item, then new_item *is* the list */ + return new_item; +} + +void curl_slist_free_all( struct curl_slist * list ) +{ + if ( list ) + { + struct curl_slist* item = list; + struct curl_slist* next = NULL; + do + { + next = item->next; + if ( item->data ) + free( item->data ); + free(item); + item = next; + } while ( next ); + } +} + +void curl_free( void * p ) +{ + free( p ); +} + +CURLcode curl_global_init( long ) +{ + return CURLE_OK; +} + +CURL *curl_easy_init( void ) +{ + return new CurlHandle(); +} + +void curl_easy_cleanup( CURL * curl ) +{ + CurlHandle* handle = static_cast< CurlHandle* >( curl ); + delete( handle ); +} + +void curl_easy_reset( CURL * curl ) +{ + CurlHandle* handle = static_cast< CurlHandle * >( curl ); + handle->reset( ); +} + +char *curl_easy_escape( CURL *, const char *string, int length ) +{ + return strndup( string, length ); +} + +char *curl_unescape( const char *string, int length ) +{ + return strndup( string, length ); +} + +char *curl_easy_unescape( CURL *, const char *string, int length, int * ) +{ + return curl_unescape( string, length ); +} + +CURLcode curl_easy_setopt( CURL * curl, long option, ... ) +{ + CurlHandle* handle = static_cast< CurlHandle * >( curl ); + + va_list arg; + va_start( arg, option ); + switch ( option ) + { + /* TODO Add support for more options */ + case CURLOPT_POST: + { + if ( va_arg( arg, long ) ) + handle->m_method = string( "POST" ); + break; + } + case CURLOPT_UPLOAD: + { + if ( 0 != va_arg( arg, long ) ) + handle->m_method = string( "PUT" ); + break; + } + case CURLOPT_CUSTOMREQUEST: + handle->m_method = string( va_arg( arg, char* ) ); + break; + case CURLOPT_URL: + { + handle->m_url = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_WRITEFUNCTION: + { + handle->m_writeFn = va_arg( arg, write_callback ); + break; + } + case CURLOPT_WRITEDATA: + { + handle->m_writeData = va_arg( arg, void* ); + break; + } + case CURLOPT_READFUNCTION: + { + handle->m_readFn = va_arg( arg, read_callback ); + break; + } + case CURLOPT_READDATA: + { + handle->m_readData = va_arg( arg, void* ); + break; + } + case CURLOPT_INFILESIZE: + { + handle->m_readSize = va_arg( arg, long ); + break; + } + case CURLOPT_HEADERFUNCTION: + { + handle->m_headersFn = va_arg( arg, headers_callback ); + break; + } + case CURLOPT_WRITEHEADER: + { + handle->m_headersData = va_arg( arg, void* ); + break; + } + case CURLOPT_USERNAME: + { + handle->m_username = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_PASSWORD: + { + handle->m_password = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_USERPWD: + { + string userpwd( va_arg( arg, char* ) ); + size_t pos = userpwd.find( ':' ); + if ( pos != string::npos ) + { + handle->m_username = userpwd.substr( 0, pos ); + handle->m_password = userpwd.substr( pos + 1 ); + } + break; + } + case CURLOPT_PROXY: + { + // FIXME curl does some more complex things with port and type + handle->m_proxy = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_NOPROXY: + { + handle->m_noProxy = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_PROXYUSERNAME: + { + handle->m_proxyUser = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_PROXYPASSWORD: + { + handle->m_proxyPass = string( va_arg( arg, char* ) ); + break; + } + case CURLOPT_PROXYUSERPWD: + { + string userpwd( va_arg( arg, char* ) ); + size_t pos = userpwd.find( ':' ); + if ( pos != string::npos ) + { + handle->m_proxyUser = userpwd.substr( 0, pos ); + handle->m_proxyPass = userpwd.substr( pos + 1 ); + } + break; + } + case CURLOPT_SSL_VERIFYHOST: + { + handle->m_verifyHost = va_arg( arg, long ) != 0; + break; + } + case CURLOPT_SSL_VERIFYPEER: + { + handle->m_verifyPeer = va_arg( arg, long ) != 0; + break; + } + case CURLOPT_CERTINFO: + { + handle->m_certInfo = va_arg( arg, long ); + break; + } + case CURLOPT_HTTPHEADER: + { + handle->m_headers.clear(); + struct curl_slist* headers = va_arg( arg, struct curl_slist* ); + while ( headers != NULL ) + { + handle->m_headers.push_back( string( headers->data ) ); + headers = headers->next; + } + break; + } + default: + { + // We surely don't want to break the test for that. + } + } + va_end( arg ); + + return CURLE_OK; +} + +CURLcode curl_easy_perform( CURL * curl ) +{ + CurlHandle* handle = static_cast< CurlHandle * >( curl ); + + /* Fake a bad SSL Certificate? */ + if ( !mockup::config->m_badSSLCertificate.empty( ) && handle->m_verifyPeer && handle->m_verifyHost ) + { + return CURLE_SSL_CACERT; + } + + /* Populate the certificate infos */ + if ( handle->m_certInfo && !mockup::config->m_badSSLCertificate.empty( ) ) + { + curl_slist * certData = NULL; + string cert( "Cert:" + mockup::config->m_badSSLCertificate ); + char* c_cert = strdup( cert.c_str( ) ); + certData = curl_slist_append( certData, c_cert ); + free( c_cert ); + + handle->m_certs.num_of_certs = 1; + handle->m_certs.certinfo = ( struct curl_slist** )calloc( ( size_t )1, sizeof( struct curl_slist * ) ); + handle->m_certs.certinfo[0] = certData; + } + + /* Check the credentials */ + if ( mockup::config->hasCredentials( ) && + ( handle->m_username != mockup::config->m_username || + handle->m_password != mockup::config->m_password ) ) + { + // Send HTTP 401 + handle->m_httpError = 401; + return CURLE_HTTP_RETURNED_ERROR; + } + + // Store the requests for later verifications + stringstream body; + if ( handle->m_readFn && handle->m_readData ) + { + size_t bufSize = 2048; + char* buf = new char[bufSize]; + + size_t read = 0; + do + { + read = handle->m_readFn( buf, 1, bufSize, handle->m_readData ); + body.write( buf, read ); + } while ( read == bufSize ); + + delete[] buf; + } + + mockup::config->m_requests.push_back( mockup::Request( handle->m_url, handle->m_method, body.str( ), handle->m_headers ) ); + + + return mockup::config->writeResponse( handle ); +} + +CURLcode curl_easy_getinfo( CURL * curl, long info, ... ) +{ + CurlHandle* handle = static_cast< CurlHandle * >( curl ); + + va_list arg; + va_start( arg, info ); + switch ( info ) + { + case CURLINFO_RESPONSE_CODE: + { + long* buf = va_arg( arg, long* ); + *buf = handle->m_httpError; + break; + } + case CURLINFO_CERTINFO: + { + struct curl_slist** param = va_arg( arg, struct curl_slist** ); + if ( NULL != param ) + { + union + { + struct curl_certinfo * to_certinfo; + struct curl_slist * to_slist; + } ptr; + + // Fill it + ptr.to_certinfo = &handle->m_certs; + *param = ptr.to_slist; + } + break; + } + default: + { + // We surely don't want to break the test for that. + } + } + va_end( arg ); + + return CURLE_OK; +} + +CurlHandle::CurlHandle( ) : + m_url( ), + m_writeFn( NULL ), + m_writeData( NULL ), + m_readFn( NULL ), + m_readData( NULL ), + m_readSize( 0 ), + m_headersFn( NULL ), + m_headersData( NULL ), + m_username( ), + m_password( ), + m_proxy( ), + m_noProxy( ), + m_proxyUser( ), + m_proxyPass( ), + m_verifyHost( true ), + m_verifyPeer( true ), + m_certInfo( false ), + m_certs( ), + m_httpError( 0 ), + m_method( "GET" ), + m_headers( ) +{ +} + +CurlHandle::CurlHandle( const CurlHandle& copy ) : + m_url( copy.m_url ), + m_writeFn( copy.m_writeFn ), + m_writeData( copy.m_writeData ), + m_readFn( copy.m_readFn ), + m_readData( copy.m_readData ), + m_readSize( copy.m_readSize ), + m_headersFn( copy.m_headersFn ), + m_headersData( copy.m_headersData ), + m_username( copy.m_username ), + m_password( copy.m_password ), + m_proxy( copy.m_proxy ), + m_noProxy( copy.m_noProxy ), + m_proxyUser( copy.m_proxyUser ), + m_proxyPass( copy.m_proxyPass ), + m_verifyHost( copy.m_verifyHost ), + m_verifyPeer( copy.m_verifyPeer ), + m_certInfo( copy.m_certInfo ), + m_certs( copy.m_certs ), + m_httpError( copy.m_httpError ), + m_method( copy.m_method ), + m_headers( copy.m_headers ) +{ +} + +CurlHandle& CurlHandle::operator=( const CurlHandle& copy ) +{ + if ( this != © ) + { + m_url = copy.m_url; + m_writeFn = copy.m_writeFn; + m_writeData = copy.m_writeData; + m_readFn = copy.m_readFn; + m_readData = copy.m_readData; + m_readSize = copy.m_readSize; + m_headersFn = copy.m_headersFn; + m_headersData = copy.m_headersData; + m_username = copy.m_username; + m_password = copy.m_password; + m_proxy = copy.m_proxy; + m_noProxy = copy.m_noProxy; + m_proxyUser = copy.m_proxyUser; + m_proxyPass = copy.m_proxyPass; + m_verifyHost = copy.m_verifyHost; + m_verifyPeer = copy.m_verifyPeer; + m_certInfo = copy.m_certInfo; + m_certs = copy.m_certs; + m_httpError = copy.m_httpError; + m_method = copy.m_method; + m_headers = copy.m_headers; + } + return *this; +} + +CurlHandle::~CurlHandle( ) +{ + reset(); +} + +void CurlHandle::reset( ) +{ + m_url = string( ); + m_writeFn = NULL; + m_writeData = NULL; + m_readFn = NULL; + m_readData = NULL; + m_readSize = 0; + m_username = string( ); + m_password = string( ); + m_proxy = string( ); + m_noProxy = string( ); + m_proxyUser = string( ); + m_proxyPass = string( ); + m_verifyHost = true; + m_verifyPeer = true; + m_certInfo = false; + + for ( int i = 0; i < m_certs.num_of_certs; ++i ) + { + curl_slist_free_all( m_certs.certinfo[i] ); + m_certs.certinfo[i] = NULL; + } + free( m_certs.certinfo ); + m_certs.certinfo = NULL; + m_certs.num_of_certs = 0; + + m_method = "GET"; + m_headers.clear( ); +} diff --git a/qa/mockup/curl/curl.h b/qa/mockup/curl/curl.h new file mode 100644 index 0000000..50f1cfe --- /dev/null +++ b/qa/mockup/curl/curl.h @@ -0,0 +1,153 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ +#ifndef _MOCKUP_CURL_CURL_H_ +#define _MOCKUP_CURL_CURL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Curl used symbols to mockup */ + +typedef void CURL; + +typedef enum +{ + CURLIOE_OK, /* I/O operation successful */ + CURLIOE_UNKNOWNCMD, /* command was unknown to callback */ + CURLIOE_FAILRESTART, /* failed to restart the read */ + CURLIOE_LAST /* never use */ +} curlioerr; + +#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_WIN32 (1<<1) +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) + +#define CURLOPTTYPE_LONG 0 +#define CURLOPTTYPE_OBJECTPOINT 10000 +#define CURLOPTTYPE_FUNCTIONPOINT 20000 +#define CURLOPTTYPE_OFF_T 30000 + +typedef enum +{ + CURLOPT_WRITEFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 11, + CURLOPT_READFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 12, + CURLOPT_WRITEDATA = CURLOPTTYPE_OBJECTPOINT + 1, + CURLOPT_HEADERFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 79, + CURLOPT_WRITEHEADER = CURLOPTTYPE_OBJECTPOINT + 29, + CURLOPT_FOLLOWLOCATION = CURLOPTTYPE_LONG + 52, + CURLOPT_MAXREDIRS = CURLOPTTYPE_LONG + 68, + CURLOPT_INFILESIZE = CURLOPTTYPE_LONG + 14, + CURLOPT_READDATA = CURLOPTTYPE_OBJECTPOINT + 9, + CURLOPT_UPLOAD = CURLOPTTYPE_LONG + 46, + CURLOPT_IOCTLFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 130, + CURLOPT_IOCTLDATA = CURLOPTTYPE_OBJECTPOINT + 131, + CURLOPT_HTTPHEADER = CURLOPTTYPE_OBJECTPOINT + 23, + CURLOPT_POSTFIELDSIZE = CURLOPTTYPE_LONG + 60, + CURLOPT_POST = CURLOPTTYPE_LONG + 47, + CURLOPT_CUSTOMREQUEST = CURLOPTTYPE_OBJECTPOINT + 36, + CURLOPT_URL = CURLOPTTYPE_OBJECTPOINT + 2, + CURLOPT_HTTPAUTH = CURLOPTTYPE_LONG + 107, + CURLOPT_USERNAME = CURLOPTTYPE_OBJECTPOINT + 173, + CURLOPT_PASSWORD = CURLOPTTYPE_OBJECTPOINT + 174, + CURLOPT_USERPWD = CURLOPTTYPE_OBJECTPOINT + 5, + CURLOPT_ERRORBUFFER = CURLOPTTYPE_OBJECTPOINT + 10, + CURLOPT_FAILONERROR = CURLOPTTYPE_LONG + 45, + CURLOPT_VERBOSE = CURLOPTTYPE_LONG + 41, + CURLOPT_PROXY = CURLOPTTYPE_OBJECTPOINT + 4, + CURLOPT_PROXYUSERPWD = CURLOPTTYPE_OBJECTPOINT + 6, + CURLOPT_PROXYAUTH = CURLOPTTYPE_LONG + 111, + CURLOPT_PROXYUSERNAME = CURLOPTTYPE_OBJECTPOINT + 175, + CURLOPT_PROXYPASSWORD = CURLOPTTYPE_OBJECTPOINT + 176, + CURLOPT_NOPROXY = CURLOPTTYPE_OBJECTPOINT + 177, + CURLOPT_SSL_VERIFYPEER = CURLOPTTYPE_LONG + 64, + CURLOPT_SSL_VERIFYHOST = CURLOPTTYPE_LONG + 81, + CURLOPT_CERTINFO = CURLOPTTYPE_LONG + 172 +} CURLoption; + +#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) + +typedef enum +{ + CURLE_OK = 0, + CURLE_HTTP_RETURNED_ERROR = 22, + CURLE_SSL_CACERT = 60, + /* TODO Add some more error codes from curl? */ + CURL_LAST +} CURLcode; + +struct curl_slist +{ + char *data; + struct curl_slist *next; +}; + +struct curl_slist *curl_slist_append( struct curl_slist *, const char * ); +void curl_slist_free_all( struct curl_slist * ); + +void curl_free( void *p ); +CURLcode curl_global_init( long flags ); + +CURL *curl_easy_init( void ); +void curl_easy_cleanup( CURL *curl ); +CURLcode curl_easy_setopt( CURL *curl, long option, ... ); +char *curl_easy_escape( CURL *handle, const char *string, int length ); +char *curl_unescape( const char *string, int length ); +char *curl_easy_unescape( CURL *handle, const char *string, int length, int *outlength ); +CURLcode curl_easy_perform( CURL *curl ); +void curl_easy_reset( CURL *curl ); + +struct curl_certinfo +{ + int num_of_certs; + struct curl_slist **certinfo; +}; + +#define CURLINFO_LONG 0x200000 +#define CURLINFO_SLIST 0x400000 + +typedef enum +{ + CURLINFO_NONE, + CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2, + CURLINFO_CERTINFO = CURLINFO_SLIST + 34, + CURLINFO_LASTONE = 42 +} CURLINFO; + +CURLcode curl_easy_getinfo( CURL *curl, long info, ... ); + +#define LIBCURL_VERSION_MAJOR 7 +#define LIBCURL_VERSION_MINOR 26 +#define LIBCURL_VERSION_PATCH 0 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/qa/mockup/internals.hxx b/qa/mockup/internals.hxx new file mode 100644 index 0000000..43c001e --- /dev/null +++ b/qa/mockup/internals.hxx @@ -0,0 +1,133 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#ifndef INCLUDED_QA_MOCKUP_INTERNALS_HXX +#define INCLUDED_QA_MOCKUP_INTERNALS_HXX + +#include <map> +#include <string> +#include <vector> + +typedef size_t ( *write_callback )( char *ptr, size_t size, size_t nmemb, void *userdata ); +typedef size_t ( *read_callback )( char *ptr, size_t size, size_t nmemb, void *userdata ); +typedef size_t ( *headers_callback )( char *ptr, size_t size, size_t nmemb, void *userdata ); + +class CurlHandle +{ + public: + CurlHandle( ); + CurlHandle( const CurlHandle& copy ); + CurlHandle& operator=( const CurlHandle& copy ); + ~CurlHandle( ); + + std::string m_url; + + write_callback m_writeFn; + void* m_writeData; + read_callback m_readFn; + void* m_readData; + long m_readSize; + headers_callback m_headersFn; + void* m_headersData; + + std::string m_username; + std::string m_password; + std::string m_proxy; + std::string m_noProxy; + std::string m_proxyUser; + std::string m_proxyPass; + + bool m_verifyHost; + bool m_verifyPeer; + bool m_certInfo; + + struct curl_certinfo m_certs; + + long m_httpError; + std::string m_method; + std::vector< std::string > m_headers; + + void reset( ); +}; + +namespace mockup +{ + class Response + { + public: + Response( std::string response, unsigned int status, bool isFilePath, + std::string headers ); + + std::string m_response; + unsigned int m_status; + bool m_isFilePath; + std::string m_headers; + }; + + class Request + { + public: + Request( std::string url, std::string method, std::string body, + std::vector< std::string > headers ); + + std::string m_url; + std::string m_method; + std::string m_body; + std::vector< std::string > m_headers; + }; + + class RequestMatcher + { + public: + RequestMatcher( std::string baseUrl, std::string matchParam, + std::string method, std::string matchBody ); + bool operator< ( const RequestMatcher& compare ) const; + + std::string m_baseUrl; + std::string m_matchParam; + std::string m_method; + std::string m_matchBody; + }; + + class Configuration + { + public: + Configuration( ); + + bool hasCredentials( ); + CURLcode writeResponse( CurlHandle* handle ); + + std::map< RequestMatcher, Response > m_responses; + std::vector< Request > m_requests; + std::string m_username; + std::string m_password; + std::string m_badSSLCertificate; + }; +} + +#endif diff --git a/qa/mockup/mockup-config.cxx b/qa/mockup/mockup-config.cxx new file mode 100644 index 0000000..7678518 --- /dev/null +++ b/qa/mockup/mockup-config.cxx @@ -0,0 +1,409 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include "mockup-config.h" + +#include <memory> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <iostream> + +#include <boost/algorithm/string.hpp> + +#include "internals.hxx" + +using namespace std; + +namespace +{ + void lcl_splitUrl( const string& url, string& urlBase, string& params ) + { + size_t pos = url.find( "?" ); + urlBase = url; + if ( pos != string::npos ) + { + urlBase = url.substr( 0, pos ); + params = url.substr( pos + 1 ); + } + } + + const char** lcl_toStringArray( const vector< string >& vect ) + { + const char** array = new const char*[vect.size() + 1]; + for ( size_t i = 0; i < vect.size( ); i++ ) + array[i] = vect[i].c_str(); + array[vect.size()] = NULL; + return array; + } +} + +namespace mockup +{ + Response::Response( string response, unsigned int status, bool isFilePath, string headers ) : + m_response( response ), + m_status( status ), + m_isFilePath( isFilePath ), + m_headers( headers ) + { + } + + Request::Request( string url, string method, string body, vector< string > headers ) : + m_url( url ), + m_method( method ), + m_body( body ), + m_headers( headers ) + { + } + + RequestMatcher::RequestMatcher( string baseUrl, string matchParam, string method, string matchBody ) : + m_baseUrl( baseUrl ), + m_matchParam( matchParam ), + m_method( method ), + m_matchBody( matchBody ) + { + } + + bool RequestMatcher::operator< ( const RequestMatcher& compare ) const + { + int cmpBaseUrl = m_baseUrl.compare( compare.m_baseUrl ) ; + if ( cmpBaseUrl != 0 ) + return cmpBaseUrl < 0; + + int cmpMatchParam = m_matchParam.compare( compare.m_matchParam ); + if ( cmpMatchParam != 0 ) + return cmpMatchParam < 0; + + int cmpMatchMethod = m_method.compare( compare.m_method ); + if ( cmpMatchMethod != 0 ) + return cmpMatchMethod < 0; + + int cmpMatchBody = m_matchBody.compare( compare.m_matchBody ); + return cmpMatchBody < 0; + } + + Configuration::Configuration( ) : + m_responses( ), + m_requests( ), + m_username( ), + m_password( ), + m_badSSLCertificate( ) + { + } + + bool Configuration::hasCredentials( ) + { + return !m_username.empty( ) && !m_password.empty( ); + } + + /** Find a suitable response + * using the request as a search key + */ + CURLcode Configuration::writeResponse( CurlHandle* handle ) + { + CURLcode code = CURLE_OK; + + string headers; + string response; + bool foundResponse = false; + bool isFilePath = true; + const string& url = handle->m_url; + + string urlBase = url; + string params; + lcl_splitUrl( url, urlBase, params ); + string method = handle->m_method; + string body = m_requests.back().m_body; + + for ( map< RequestMatcher, Response >::iterator it = m_responses.begin( ); + it != m_responses.end( ) && response.empty( ); ++it ) + { + RequestMatcher matcher = it->first; + string& paramFind = matcher.m_matchParam; + bool matchBaseUrl = matcher.m_baseUrl.empty() || ( urlBase == matcher.m_baseUrl ); + bool matchParams = paramFind.empty( ) || ( params.find( paramFind ) != string::npos ); + bool matchMethod = it->first.m_method.empty( ) || ( it->first.m_method == method ); + bool matchBody = matcher.m_matchBody.empty( ) || ( body.find( matcher.m_matchBody ) != string::npos ); + + if ( matchBaseUrl && matchParams && matchMethod && matchBody ) + { + foundResponse = true; + response = it->second.m_response; + handle->m_httpError = it->second.m_status; + isFilePath = it->second.m_isFilePath; + headers = it->second.m_headers; + } + } + + // Output headers is any + if ( !headers.empty() ) + { + char* buf = strdup( headers.c_str() ); + handle->m_headersFn( buf, 1, headers.size( ), handle->m_headersData ); + free( buf ); + } + + // If nothing matched, then send a 404 HTTP error instead + if ( !foundResponse || ( foundResponse && isFilePath && response.empty() ) ) + handle->m_httpError = 404; + else + { + if ( isFilePath ) + { + FILE* fd = fopen( response.c_str( ), "r" ); + if ( !fd ) { + cerr << "Missing test file: " << response << endl; + handle->m_httpError = 500; + return CURLE_HTTP_RETURNED_ERROR; + } + + + size_t bufSize = 2048; + char* buf = new char[bufSize]; + + size_t read = 0; + size_t written = 0; + do + { + read = fread( buf, 1, bufSize, fd ); + written = handle->m_writeFn( buf, 1, read, handle->m_writeData ); + } while ( read == bufSize && written == read ); + + fclose( fd ); + delete[] buf; + } + else + { + if ( !response.empty() ) + { + char* buf = strdup( response.c_str() ); + handle->m_writeFn( buf, 1, response.size( ), handle->m_writeData ); + free( buf ); + } + } + } + + // What curl error code to give? + if ( handle->m_httpError == 0 ) + handle->m_httpError = 200; + + if ( handle->m_httpError < 200 || handle->m_httpError >= 300 ) + code = CURLE_HTTP_RETURNED_ERROR; + + return code; + } + + unique_ptr<Configuration> config{ new Configuration( ) }; +} + +void curl_mockup_reset( ) +{ + mockup::config.reset( new mockup::Configuration( ) ); +} + +void curl_mockup_addResponse( const char* urlBase, const char* matchParam, const char* method, + const char* response, unsigned int status, bool isFilePath, + const char* headers, const char* matchBody ) +{ + string matchBodyStr; + if ( matchBody ) + matchBodyStr = matchBody; + mockup::RequestMatcher matcher( urlBase, matchParam, method, matchBodyStr ); + map< mockup::RequestMatcher, mockup::Response >::iterator it = mockup::config->m_responses.find( matcher ); + if ( it != mockup::config->m_responses.end( ) ) + mockup::config->m_responses.erase( it ); + + string headersStr; + if ( headers != NULL ) + headersStr = headers; + mockup::Response responseDesc( response, status, isFilePath, headersStr ); + mockup::config->m_responses.insert( pair< mockup::RequestMatcher, mockup::Response >( matcher, responseDesc ) ); +} + +void curl_mockup_setResponse( const char* filepath ) +{ + mockup::config->m_responses.clear( ); + curl_mockup_addResponse( "", "", "", filepath ); +} + +void curl_mockup_setCredentials( const char* username, const char* password ) +{ + mockup::config->m_username = string( username ); + mockup::config->m_password = string( password ); +} + +const struct HttpRequest* curl_mockup_getRequest( const char* urlBase, + const char* matchParam, + const char* method, + const char* matchBody ) +{ + struct HttpRequest* request = NULL; + + string urlBaseString( urlBase ); + string matchParamString( matchParam ); + + string matchBodyStr; + if ( matchBody ) + matchBodyStr = matchBody; + + for ( vector< mockup::Request >::iterator it = mockup::config->m_requests.begin( ); + it != mockup::config->m_requests.end( ) && request == NULL; ++it ) + { + string url; + string params; + if ( it->m_method == string( method ) ) + { + lcl_splitUrl( it->m_url, url, params ); + + bool matchBaseUrl = urlBaseString.empty() || boost::starts_with( url, urlBaseString ); + bool matchParams = matchParamString.empty( ) || ( params.find( matchParamString ) != string::npos ); + bool matchBodyPart = !matchBody || ( it->m_body.find( matchBodyStr ) != string::npos ); + + if ( matchBaseUrl && matchParams && matchBodyPart ) + { + request = new HttpRequest; + request->url = it->m_url.c_str(); + request->body = it->m_body.c_str(); + request->headers = lcl_toStringArray( it->m_headers ); + } + } + } + + return request; +} + +const char* curl_mockup_getRequestBody( const char* urlBase, + const char* matchParam, + const char* method, + const char* matchBody ) +{ + const struct HttpRequest* request = curl_mockup_getRequest( urlBase, matchParam, method, matchBody ); + if ( request ) + { + const char* body = request->body; + curl_mockup_HttpRequest_free( request ); + return body; + } + return NULL; +} + +int curl_mockup_getRequestsCount( const char* urlBase, + const char* matchParam, + const char* method, + const char* matchBody ) +{ + int count = 0; + + string urlBaseString( urlBase ); + string matchParamString( matchParam ); + string matchBodyStr( matchBody ); + + for ( vector< mockup::Request >::iterator it = mockup::config->m_requests.begin( ); + it != mockup::config->m_requests.end( ); ++it ) + { + string url; + string params; + if ( it->m_method == string( method ) ) + { + lcl_splitUrl( it->m_url, url, params ); + + bool matchBaseUrl = urlBaseString.empty() || boost::starts_with( url, urlBaseString ); + bool matchParams = matchParamString.empty( ) || + ( params.find( matchParamString ) != string::npos ); + bool matchBodyPart = matchBodyStr.empty() || + ( it->m_body.find( matchBodyStr ) != string::npos ); + + if ( matchBaseUrl && matchParams && matchBodyPart ) + { + count++; + } + } + } + return count; +} + +void curl_mockup_HttpRequest_free( const struct HttpRequest* request ) +{ + delete[] request->headers; + delete request; +} + +char* curl_mockup_HttpRequest_getHeader( const struct HttpRequest* request, const char* name ) +{ + char* value = NULL; + size_t i = 0; + while ( request->headers[i] != NULL && value == NULL ) + { + string header = request->headers[i]; + const string prefix = string( name ) + ":"; + size_t pos = header.find( prefix ); + if ( pos == 0 ) + { + value = strdup( header.substr( prefix.size() ).c_str() ); + } + ++i; + } + return value; +} + +const char* curl_mockup_getProxy( CURL* curl ) +{ + CurlHandle* handle = static_cast< CurlHandle* >( curl ); + if ( NULL != handle ) + return handle->m_proxy.c_str(); + return NULL; +} + +const char* curl_mockup_getNoProxy( CURL* curl ) +{ + CurlHandle* handle = static_cast< CurlHandle* >( curl ); + if ( NULL != handle ) + return handle->m_noProxy.c_str(); + return NULL; +} + +const char* curl_mockup_getProxyUser( CURL* curl ) +{ + CurlHandle* handle = static_cast< CurlHandle* >( curl ); + if ( NULL != handle ) + return handle->m_proxyUser.c_str(); + return NULL; +} + +const char* curl_mockup_getProxyPass( CURL* curl ) +{ + CurlHandle* handle = static_cast< CurlHandle* >( curl ); + if ( NULL != handle ) + return handle->m_proxyPass.c_str(); + return NULL; +} + +void curl_mockup_setSSLBadCertificate( const char* certificate ) +{ + mockup::config->m_badSSLCertificate = string( certificate ); +} diff --git a/qa/mockup/mockup-config.h b/qa/mockup/mockup-config.h new file mode 100644 index 0000000..d0fc3bb --- /dev/null +++ b/qa/mockup/mockup-config.h @@ -0,0 +1,113 @@ +/* libcmis + * Version: MPL 1.1 / GPLv2+ / LGPLv2+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 SUSE <cbosdonnat@suse.com> + * + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPLv2+"), or + * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), + * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable + * instead of those above. + */ + +#include <curl/curl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Mockup behavior configuration functions */ +void curl_mockup_reset( ); + +/** Add a new HTTP response the server should send. + + \param baseURL + the base URL of the request without parameters + \param matchParam + a string to find in the parameters part of the URL to match + \param method + HTTP method to match like PUT, GET, POST or DELETE. An empty + string matches any method. + \param response + a string corresponding either to the file path of the request + body to send or directly the content to send. This value has + a different meaning depending on isFilePath parameter. + \param status + the HTTP status to return. 0 means HTTP OK (200). + \param isFilePath + if this value is true the response value is used as a file path, + otherwise, the response value is used as the body of the HTTP + response to send. + \param headers + the HTTP headers block to send with the response. By default + no header is sent. + \param matchBody + a string to find in the request body to match + */ +void curl_mockup_addResponse( const char* baseUrl, const char* matchParam, const char* method, + const char* response, unsigned int status = 0, bool isFilePath = true, + const char* headers = 0, const char* matchBody = 0 ); + +/** Set the HTTP response the server is supposed to send. + This will reset all already defined responses. + */ +void curl_mockup_setResponse( const char* filepath ); +void curl_mockup_setCredentials( const char* username, const char* password ); + +struct HttpRequest +{ + const char* url; + const char* body; + ///< NULL terminated array of headers. + const char** headers; +}; + +const struct HttpRequest* curl_mockup_getRequest( const char* baseUrl, + const char* matchParam, + const char* method, + const char* matchBody = 0 ); +const char* curl_mockup_getRequestBody( const char* baseUrl, + const char* matchParam, + const char* method, + const char* matchBody = 0 ); +int curl_mockup_getRequestsCount( const char* urlBase, + const char* matchParam, + const char* method, + const char* matchBody = "" ); + +void curl_mockup_HttpRequest_free( const struct HttpRequest* request ); + +/** The resulting value is either NULL (no such header found) or the value + of the header. In such a case, the result needs to be freed by the caller. + */ +char* curl_mockup_HttpRequest_getHeader( const struct HttpRequest* request, const char* name ); + +const char* curl_mockup_getProxy( CURL* handle ); +const char* curl_mockup_getNoProxy( CURL* handle ); +const char* curl_mockup_getProxyUser( CURL* handle ); +const char* curl_mockup_getProxyPass( CURL* handle ); + +/** Set a fake invalid certificate to raise CURLE_SSL_CACERT. Setting it + to an empty string will reset to no certificate. + */ +void curl_mockup_setSSLBadCertificate( const char* certificate ); + +#ifdef __cplusplus +} +#endif |