Changeset 2428


Ignore:
Timestamp:
03/01/2009 08:59:47 (3 years ago)
Author:
chris
Message:

Add ability to send an HTTPRequest to a socket and to parse an
HTTPResponse from a socket, to create a simple HTTP client.

Location:
box/trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • box/trunk/lib/httpserver/HTTPException.txt

    r926 r2428  
    22 
    33Internal                                                        0 
    4 RequestReadFailed                                       1 
    5 RequestAlreadyBeenRead                          2 
     4RequestReadFailed                                               1 
     5RequestAlreadyBeenRead                                          2 
    66BadRequest                                                      3 
    7 UnknownResponseCodeUsed                         4 
    8 NoContentTypeSet                                        5 
    9 POSTContentTooLong                                      6 
    10 CannotSetRedirectIfReponseHasData       7 
    11 CannotSetNotFoundIfReponseHasData       8 
    12 NotImplemented                                          9 
     7UnknownResponseCodeUsed                                         4 
     8NoContentTypeSet                                                5 
     9POSTContentTooLong                                              6 
     10CannotSetRedirectIfReponseHasData                               7 
     11CannotSetNotFoundIfReponseHasData                               8 
     12NotImplemented                                                  9 
     13RequestNotInitialised                                           10 
     14BadResponse                                                     11 
     15ResponseReadFailed                                              12 
  • box/trunk/lib/httpserver/HTTPQueryDecoder.cpp

    r926 r2428  
    2020// 
    2121// Function 
    22 //              Name:    HTTPQueryDecoder::HTTPQueryDecoder(HTTPRequest::Query_t &) 
    23 //              Purpose: Constructor. Pass in the query contents you want to decode 
    24 //                               the query string into. 
     22//              Name:    HTTPQueryDecoder::HTTPQueryDecoder( 
     23//                       HTTPRequest::Query_t &) 
     24//              Purpose: Constructor. Pass in the query contents you want 
     25//                       to decode the query string into. 
    2526//              Created: 26/3/04 
    2627// 
  • box/trunk/lib/httpserver/HTTPRequest.cpp

    r2423 r2428  
    5353// 
    5454// Function 
     55//              Name:    HTTPRequest::HTTPRequest(enum Method, 
     56//                       const std::string&) 
     57//              Purpose: Alternate constructor for hand-crafted requests 
     58//              Created: 03/01/09 
     59// 
     60// -------------------------------------------------------------------------- 
     61HTTPRequest::HTTPRequest(enum Method method, const std::string& rURI) 
     62        : mMethod(method), 
     63          mRequestURI(rURI), 
     64          mHostPort(80),        // default if not specified 
     65          mHTTPVersion(HTTPVersion_1_1), 
     66          mContentLength(-1), 
     67          mpCookies(0), 
     68          mClientKeepAliveRequested(false) 
     69{ 
     70} 
     71 
     72 
     73 
     74// -------------------------------------------------------------------------- 
     75// 
     76// Function 
    5577//              Name:    HTTPRequest::~HTTPRequest() 
    5678//              Purpose: Destructor 
     
    7395// Function 
    7496//              Name:    HTTPRequest::Read(IOStreamGetLine &, int) 
    75 //              Purpose: Read the request from an IOStreamGetLine (and attached stream) 
    76 //                               Returns false if there was no valid request, probably due to  
    77 //                               a kept-alive connection closing. 
     97//              Purpose: Read the request from an IOStreamGetLine (and 
     98//                       attached stream). 
     99//                       Returns false if there was no valid request, 
     100//                       probably due to a kept-alive connection closing. 
    78101//              Created: 26/3/04 
    79102// 
     
    284307        } 
    285308         
     309        return true; 
     310} 
     311 
     312// -------------------------------------------------------------------------- 
     313// 
     314// Function 
     315//              Name:    HTTPRequest::Write(IOStream &, int) 
     316//              Purpose: Write the request to an IOStream using HTTP. 
     317//              Created: 03/01/09 
     318// 
     319// -------------------------------------------------------------------------- 
     320bool HTTPRequest::Write(IOStream &rStream, int Timeout) 
     321{ 
     322        switch (mMethod) 
     323        { 
     324        case Method_UNINITIALISED: 
     325                THROW_EXCEPTION(HTTPException, RequestNotInitialised); break; 
     326        case Method_UNKNOWN: 
     327                THROW_EXCEPTION(HTTPException, BadRequest); break; 
     328        case Method_GET: 
     329                rStream.Write("GET"); break; 
     330        case Method_HEAD: 
     331                rStream.Write("HEAD"); break; 
     332        case Method_POST: 
     333                rStream.Write("POST"); break; 
     334        } 
     335 
     336        rStream.Write(" "); 
     337        rStream.Write(mRequestURI.c_str()); 
     338        rStream.Write(" "); 
     339 
     340        switch (mHTTPVersion) 
     341        { 
     342        case HTTPVersion_0_9: rStream.Write("HTTP/0.9"); break; 
     343        case HTTPVersion_1_0: rStream.Write("HTTP/1.0"); break; 
     344        case HTTPVersion_1_1: rStream.Write("HTTP/1.1"); break; 
     345        default: 
     346                THROW_EXCEPTION(HTTPException, NotImplemented); 
     347        } 
     348 
     349        rStream.Write("\n"); 
     350        std::ostringstream oss; 
     351 
     352        if (mContentLength != -1) 
     353        { 
     354                oss << "Content-Length: " << mContentLength << "\n"; 
     355        } 
     356 
     357        if (mContentType != "") 
     358        { 
     359                oss << "Content-Type: " << mContentType << "\n"; 
     360        } 
     361 
     362        if (mHostName != "") 
     363        { 
     364                if (mHostPort != 80) 
     365                { 
     366                        oss << "Host: " << mHostName << ":" << mHostPort << 
     367                                "\n"; 
     368                } 
     369                else 
     370                { 
     371                        oss << "Host: " << mHostName << "\n"; 
     372                } 
     373        } 
     374 
     375        if (mpCookies) 
     376        { 
     377                THROW_EXCEPTION(HTTPException, NotImplemented); 
     378        } 
     379 
     380        if (mClientKeepAliveRequested) 
     381        { 
     382                oss << "Connection: keep-alive\n"; 
     383        } 
     384        else 
     385        { 
     386                oss << "Connection: close\n"; 
     387        } 
     388 
     389        rStream.Write(oss.str().c_str()); 
     390        rStream.Write("\n"); 
     391 
    286392        return true; 
    287393} 
  • box/trunk/lib/httpserver/HTTPRequest.h

    r926 r2428  
    2828{ 
    2929public: 
     30        enum Method 
     31        { 
     32                Method_UNINITIALISED = -1, 
     33                Method_UNKNOWN = 0, 
     34                Method_GET = 1, 
     35                Method_HEAD = 2, 
     36                Method_POST = 3 
     37        }; 
     38         
    3039        HTTPRequest(); 
     40        HTTPRequest(enum Method method, const std::string& rURI); 
    3141        ~HTTPRequest(); 
    3242private: 
     
    4151        enum 
    4252        { 
    43                 Method_UNINITIALISED = -1, 
    44                 Method_UNKNOWN = 0, 
    45                 Method_GET = 1, 
    46                 Method_HEAD = 2, 
    47                 Method_POST = 3 
    48         }; 
    49          
    50         enum 
    51         { 
    5253                HTTPVersion__MajorMultiplier = 1000, 
    5354                HTTPVersion_0_9 = 9, 
     
    5758 
    5859        bool Read(IOStreamGetLine &rGetLine, int Timeout); 
     60        bool Write(IOStream &rStream, int Timeout); 
    5961 
    6062        typedef std::map<std::string, std::string> CookieJar_t; 
     
    6870        // 
    6971        // -------------------------------------------------------------------------- 
    70         int GetMethod() const {return mMethod;} 
     72        enum Method GetMethod() const {return mMethod;} 
    7173        const std::string &GetRequestURI() const {return mRequestURI;} 
    7274        const std::string &GetHostName() const {return mHostName;}      // note: request does splitting of Host: header 
     
    9294        // -------------------------------------------------------------------------- 
    9395        bool GetClientKeepAliveRequested() const {return mClientKeepAliveRequested;} 
     96        void SetClientKeepAliveRequested(bool keepAlive) 
     97        { 
     98                mClientKeepAliveRequested = keepAlive; 
     99        } 
    94100 
    95101private: 
     
    98104 
    99105private: 
    100         int mMethod; 
     106        enum Method mMethod; 
    101107        std::string mRequestURI; 
    102108        std::string mHostName; 
  • box/trunk/lib/httpserver/HTTPResponse.cpp

    r926 r2428  
    1414 
    1515#include "HTTPResponse.h" 
     16#include "IOStreamGetLine.h" 
    1617#include "autogen_HTTPException.h" 
    1718 
     
    3334        : mResponseCode(HTTPResponse::Code_NoContent), 
    3435          mResponseIsDynamicContent(true), 
    35           mKeepAlive(false) 
     36          mKeepAlive(false), 
     37          mContentLength(-1) 
    3638{ 
    3739} 
     
    5557// Function 
    5658//              Name:    HTTPResponse::ResponseCodeToString(int) 
    57 //              Purpose: Return string equivalent of the response code, suitable for Status: headers 
     59//              Purpose: Return string equivalent of the response code, 
     60//                       suitable for Status: headers 
    5861//              Created: 26/3/04 
    5962// 
     
    115118// Function 
    116119//              Name:    HTTPResponse::Send(IOStream &, bool) 
    117 //              Purpose: Build the response, and send via the stream. Optionally omitting 
    118 //                              the content. 
     120//              Purpose: Build the response, and send via the stream. 
     121//                       Optionally omitting the content. 
    119122//              Created: 26/3/04 
    120123// 
     
    140143                } 
    141144                // Extra headers... 
    142                 for(std::vector<std::string>::const_iterator i(mExtraHeaders.begin()); i != mExtraHeaders.end(); ++i) 
     145                for(std::vector<std::pair<std::string, std::string> >::const_iterator i(mExtraHeaders.begin()); i != mExtraHeaders.end(); ++i) 
    143146                { 
    144147                        header += "\r\n"; 
    145                         header += *i; 
     148                        header += i->first + ": " + i->second; 
    146149                } 
    147150                // NOTE: a line ending must be included here in all cases 
     
    182185// 
    183186// Function 
     187//              Name:    HTTPResponse::ParseHeaders(IOStreamGetLine &, int) 
     188//              Purpose: Private. Parse the headers of the response 
     189//              Created: 26/3/04 
     190// 
     191// -------------------------------------------------------------------------- 
     192void HTTPResponse::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout) 
     193{ 
     194        std::string header; 
     195        bool haveHeader = false; 
     196        while(true) 
     197        { 
     198                if(rGetLine.IsEOF()) 
     199                { 
     200                        // Header terminates unexpectedly 
     201                        THROW_EXCEPTION(HTTPException, BadRequest)               
     202                } 
     203 
     204                std::string currentLine;         
     205                if(!rGetLine.GetLine(currentLine, false /* no preprocess */, Timeout)) 
     206                { 
     207                        // Timeout 
     208                        THROW_EXCEPTION(HTTPException, RequestReadFailed) 
     209                } 
     210                 
     211                // Is this a continuation of the previous line? 
     212                bool processHeader = haveHeader; 
     213                if(!currentLine.empty() && (currentLine[0] == ' ' || currentLine[0] == '\t')) 
     214                { 
     215                        // A continuation, don't process anything yet 
     216                        processHeader = false; 
     217                } 
     218                //TRACE3("%d:%d:%s\n", processHeader, haveHeader, currentLine.c_str()); 
     219                 
     220                // Parse the header -- this will actually process the header 
     221                // from the previous run around the loop. 
     222                if(processHeader) 
     223                { 
     224                        // Find where the : is in the line 
     225                        const char *h = header.c_str(); 
     226                        int p = 0; 
     227                        while(h[p] != '\0' && h[p] != ':') 
     228                        { 
     229                                ++p; 
     230                        } 
     231                        // Skip white space 
     232                        int dataStart = p + 1; 
     233                        while(h[dataStart] == ' ' || h[dataStart] == '\t') 
     234                        { 
     235                                ++dataStart; 
     236                        } 
     237                 
     238                        if(p == sizeof("Content-Length")-1 
     239                                && ::strncasecmp(h, "Content-Length", sizeof("Content-Length")-1) == 0) 
     240                        { 
     241                                // Decode number 
     242                                long len = ::strtol(h + dataStart, NULL, 10);   // returns zero in error case, this is OK 
     243                                if(len < 0) len = 0; 
     244                                // Store 
     245                                mContentLength = len; 
     246                        } 
     247                        else if(p == sizeof("Content-Type")-1 
     248                                && ::strncasecmp(h, "Content-Type", sizeof("Content-Type")-1) == 0) 
     249                        { 
     250                                // Store rest of string as content type 
     251                                mContentType = h + dataStart; 
     252                        } 
     253                        else if(p == sizeof("Cookie")-1 
     254                                && ::strncasecmp(h, "Cookie", sizeof("Cookie")-1) == 0) 
     255                        { 
     256                                THROW_EXCEPTION(HTTPException, NotImplemented); 
     257                                /* 
     258                                // Parse cookies 
     259                                ParseCookies(header, dataStart); 
     260                                */ 
     261                        } 
     262                        else if(p == sizeof("Connection")-1 
     263                                && ::strncasecmp(h, "Connection", sizeof("Connection")-1) == 0) 
     264                        { 
     265                                // Connection header, what is required? 
     266                                const char *v = h + dataStart; 
     267                                if(::strcasecmp(v, "close") == 0) 
     268                                { 
     269                                        mKeepAlive = false; 
     270                                } 
     271                                else if(::strcasecmp(v, "keep-alive") == 0) 
     272                                { 
     273                                        mKeepAlive = true; 
     274                                } 
     275                                // else don't understand, just assume default for protocol version 
     276                        } 
     277                        else 
     278                        { 
     279                                std::string headerName = header.substr(0, p); 
     280                                AddHeader(headerName, h + dataStart); 
     281                        } 
     282                         
     283                        // Unset have header flag, as it's now been processed 
     284                        haveHeader = false; 
     285                } 
     286 
     287                // Store the chunk of header the for next time round 
     288                if(haveHeader) 
     289                { 
     290                        header += currentLine; 
     291                } 
     292                else 
     293                { 
     294                        header = currentLine; 
     295                        haveHeader = true; 
     296                } 
     297 
     298                // End of headers? 
     299                if(currentLine.empty()) 
     300                { 
     301                        // All done! 
     302                        break; 
     303                }                
     304        } 
     305} 
     306 
     307void HTTPResponse::Receive(IOStream& rStream, int Timeout) 
     308{ 
     309        IOStreamGetLine rGetLine(rStream); 
     310 
     311        if(rGetLine.IsEOF()) 
     312        { 
     313                // Connection terminated unexpectedly 
     314                THROW_EXCEPTION(HTTPException, BadResponse)              
     315        } 
     316 
     317        std::string statusLine;  
     318        if(!rGetLine.GetLine(statusLine, false /* no preprocess */, Timeout)) 
     319        { 
     320                // Timeout 
     321                THROW_EXCEPTION(HTTPException, ResponseReadFailed) 
     322        } 
     323 
     324        if (statusLine.substr(0, 7) != "HTTP/1." || 
     325                statusLine[8] != ' ') 
     326        { 
     327                // Status line terminated unexpectedly 
     328                BOX_ERROR("Bad response status line: " << statusLine); 
     329                THROW_EXCEPTION(HTTPException, BadResponse)              
     330        } 
     331 
     332        if (statusLine[5] == '1' && statusLine[7] == '1') 
     333        { 
     334                // HTTP/1.1 default is to keep alive 
     335                mKeepAlive = true; 
     336        } 
     337                 
     338        // Decode the status code 
     339        long status = ::strtol(statusLine.substr(9, 3).c_str(), NULL, 10); 
     340        // returns zero in error case, this is OK 
     341        if(status < 0) status = 0; 
     342        // Store 
     343        mResponseCode = status; 
     344 
     345        ParseHeaders(rGetLine, Timeout); 
     346 
     347        // push back whatever bytes we have left 
     348        // rGetLine.DetachFile(); 
     349        if (mContentLength > 0) 
     350        { 
     351                if (mContentLength < rGetLine.GetSizeOfBufferedData()) 
     352                { 
     353                        // very small response, not good! 
     354                        THROW_EXCEPTION(HTTPException, NotImplemented); 
     355                } 
     356 
     357                Write(rGetLine.GetBufferedData(), 
     358                        rGetLine.GetSizeOfBufferedData()); 
     359        } 
     360 
     361        while (mContentLength != 0) // could be -1 as well 
     362        { 
     363                char buffer[4096]; 
     364                int readSize = sizeof(buffer); 
     365                if (mContentLength > 0 && mContentLength < readSize) 
     366                { 
     367                        readSize = mContentLength; 
     368                } 
     369                readSize = rStream.Read(buffer, readSize, Timeout); 
     370                if (readSize == 0) 
     371                { 
     372                        break; 
     373                } 
     374                mContentLength -= readSize; 
     375                Write(buffer, readSize); 
     376        } 
     377 
     378        SetForReading(); 
     379} 
     380 
     381// -------------------------------------------------------------------------- 
     382// 
     383// Function 
    184384//              Name:    HTTPResponse::AddHeader(const char *) 
    185385//              Purpose: Add header, given entire line 
     
    187387// 
    188388// -------------------------------------------------------------------------- 
     389/* 
    189390void HTTPResponse::AddHeader(const char *EntireHeaderLine) 
    190391{ 
    191392        mExtraHeaders.push_back(std::string(EntireHeaderLine)); 
    192393} 
    193  
     394*/ 
    194395 
    195396// -------------------------------------------------------------------------- 
     
    201402// 
    202403// -------------------------------------------------------------------------- 
     404/* 
    203405void HTTPResponse::AddHeader(const std::string &rEntireHeaderLine) 
    204406{ 
    205407        mExtraHeaders.push_back(rEntireHeaderLine); 
    206408} 
    207  
     409*/ 
    208410 
    209411// -------------------------------------------------------------------------- 
     
    215417// 
    216418// -------------------------------------------------------------------------- 
    217 void HTTPResponse::AddHeader(const char *Header, const char *Value) 
    218 { 
    219         std::string h(Header); 
    220         h += ": "; 
    221         h += Value; 
    222         mExtraHeaders.push_back(h); 
     419void HTTPResponse::AddHeader(const char *pHeader, const char *pValue) 
     420{ 
     421        mExtraHeaders.push_back(Header(pHeader, pValue)); 
    223422} 
    224423 
     
    232431// 
    233432// -------------------------------------------------------------------------- 
    234 void HTTPResponse::AddHeader(const char *Header, const std::string &rValue) 
    235 { 
    236         std::string h(Header); 
    237         h += ": "; 
    238         h += rValue; 
    239         mExtraHeaders.push_back(h); 
     433void HTTPResponse::AddHeader(const char *pHeader, const std::string &rValue) 
     434{ 
     435        mExtraHeaders.push_back(Header(pHeader, rValue)); 
    240436} 
    241437 
     
    251447void HTTPResponse::AddHeader(const std::string &rHeader, const std::string &rValue) 
    252448{ 
    253         mExtraHeaders.push_back(rHeader + ": " + rValue); 
     449        mExtraHeaders.push_back(Header(rHeader, rValue)); 
    254450} 
    255451 
     
    280476        h += "\""; 
    281477*/ 
    282         std::string h("Set-Cookie: "); 
     478        std::string h; 
    283479        h += Name; 
    284480        h += "="; 
     
    287483        h += Path; 
    288484 
    289         mExtraHeaders.push_back(h); 
     485        mExtraHeaders.push_back(Header("Set-Cookie", h)); 
    290486} 
    291487 
     
    313509 
    314510        // Set location to redirect to 
    315         std::string header("Location: "); 
     511        std::string header; 
    316512        if(IsLocalURI) header += msDefaultURIPrefix; 
    317513        header += RedirectTo; 
    318         mExtraHeaders.push_back(header); 
     514        mExtraHeaders.push_back(Header("Location", header)); 
    319515         
    320516        // Set up some default content 
  • box/trunk/lib/httpserver/HTTPResponse.h

    r926 r2428  
    1515 
    1616#include "CollectInBufferStream.h" 
     17 
     18class IOStreamGetLine; 
    1719 
    1820// -------------------------------------------------------------------------- 
     
    3638 
    3739        void SetResponseCode(int Code); 
     40        int GetResponseCode() { return mResponseCode; } 
    3841        void SetContentType(const char *ContentType); 
     42        const std::string& GetContentType() { return mContentType; } 
    3943 
    4044        void SetAsRedirect(const char *RedirectTo, bool IsLocalURI = true); 
     
    4246 
    4347        void Send(IOStream &rStream, bool OmitContent = false); 
     48        void Receive(IOStream& rStream, int Timeout = IOStream::TimeOutInfinite); 
    4449 
    45         void AddHeader(const char *EntireHeaderLine); 
    46         void AddHeader(const std::string &rEntireHeaderLine); 
     50        // void AddHeader(const char *EntireHeaderLine); 
     51        // void AddHeader(const std::string &rEntireHeaderLine); 
    4752        void AddHeader(const char *Header, const char *Value); 
    4853        void AddHeader(const char *Header, const std::string &rValue); 
     
    107112        bool mKeepAlive; 
    108113        std::string mContentType; 
    109         std::vector<std::string> mExtraHeaders; 
     114        typedef std::pair<std::string, std::string> Header; 
     115        std::vector<Header> mExtraHeaders; 
     116        int mContentLength; // only used when reading response from stream 
    110117         
    111118        static std::string msDefaultURIPrefix; 
     119 
     120        void ParseHeaders(IOStreamGetLine &rGetLine, int Timeout); 
    112121}; 
    113122 
  • box/trunk/test/httpserver/testhttpserver.cpp

    r2423 r2428  
    1717#include "HTTPRequest.h" 
    1818#include "HTTPResponse.h" 
     19#include "IOStreamGetLine.h" 
    1920#include "ServerControl.h" 
    2021 
     
    7273                case HTTPRequest::Method_HEAD: m = "HEAD"; break; 
    7374                case HTTPRequest::Method_POST: m = "POST"; break; 
     75                default: m = "UNKNOWN"; 
    7476                } 
    7577                rResponse.Write(m, 4); 
     
    126128        int pid = LaunchServer("./test server testfiles/httpserver.conf", "testfiles/httpserver.pid"); 
    127129        TEST_THAT(pid != -1 && pid != 0); 
    128         if(pid > 0) 
    129         { 
    130                 // Run the request script 
    131                 TEST_THAT(::system("perl testfiles/testrequests.pl") == 0); 
     130        if(pid <= 0) 
     131        { 
     132                return 0; 
     133        } 
     134 
     135        // Run the request script 
     136        TEST_THAT(::system("perl testfiles/testrequests.pl") == 0); 
     137 
     138        signal(SIGPIPE, SIG_IGN); 
     139 
     140        SocketStream sock; 
     141        sock.Open(Socket::TypeINET, "localhost", 1080); 
     142 
     143        for (int i = 0; i < 4; i++) 
     144        { 
     145                HTTPRequest request(HTTPRequest::Method_GET, 
     146                        "/test-one/34/341s/234?p1=vOne&p2=vTwo"); 
     147 
     148                if (i >= 2) 
     149                { 
     150                        // first set of passes has keepalive off by default, 
     151                        // so when i == 1 the socket has already been closed 
     152                        // by the server, and we'll get -EPIPE when we try 
     153                        // to send the request. 
     154                        request.SetClientKeepAliveRequested(true); 
     155                } 
     156 
     157                if (i == 1) 
     158                { 
     159                        TEST_CHECK_THROWS(request.Write(sock, 
     160                                IOStream::TimeOutInfinite), 
     161                                ConnectionException, SocketWriteError); 
     162                        sock.Close(); 
     163                        sock.Open(Socket::TypeINET, "localhost", 1080); 
     164                        continue; 
     165                } 
     166                else 
     167                { 
     168                        request.Write(sock, IOStream::TimeOutInfinite); 
     169                } 
     170 
     171                HTTPResponse response; 
     172                response.Receive(sock); 
     173                 
     174                TEST_THAT(response.GetResponseCode() == HTTPResponse::Code_OK); 
     175                TEST_THAT(response.GetContentType() == "text/html"); 
     176 
     177                IOStreamGetLine getline(response); 
     178                std::string line; 
     179 
     180                TEST_THAT(getline.GetLine(line)); 
     181                TEST_EQUAL("<html>", line); 
     182                TEST_THAT(getline.GetLine(line)); 
     183                TEST_EQUAL("<head><title>TEST SERVER RESPONSE</title></head>", 
     184                        line); 
     185                TEST_THAT(getline.GetLine(line)); 
     186                TEST_EQUAL("<body><h1>Test response</h1>", line); 
     187                TEST_THAT(getline.GetLine(line)); 
     188                TEST_EQUAL("<p><b>URI:</b> /test-one/34/341s/234</p>", line); 
     189                TEST_THAT(getline.GetLine(line)); 
     190                TEST_EQUAL("<p><b>Query string:</b> p1=vOne&p2=vTwo</p>", line); 
     191                TEST_THAT(getline.GetLine(line)); 
     192                TEST_EQUAL("<p><b>Method:</b> GET </p>", line); 
     193                TEST_THAT(getline.GetLine(line)); 
     194                TEST_EQUAL("<p><b>Decoded query:</b><br>", line); 
     195                TEST_THAT(getline.GetLine(line)); 
     196                TEST_EQUAL("PARAM:p1=vOne<br>", line); 
     197                TEST_THAT(getline.GetLine(line)); 
     198                TEST_EQUAL("PARAM:p2=vTwo<br></p>", line); 
     199                TEST_THAT(getline.GetLine(line)); 
     200                TEST_EQUAL("<p><b>Content type:</b> </p>", line); 
     201                TEST_THAT(getline.GetLine(line)); 
     202                TEST_EQUAL("<p><b>Content length:</b> -1</p>", line); 
     203                TEST_THAT(getline.GetLine(line)); 
     204                TEST_EQUAL("<p><b>Cookies:</b><br>", line); 
     205                TEST_THAT(getline.GetLine(line)); 
     206                TEST_EQUAL("</p>", line); 
     207                TEST_THAT(getline.GetLine(line)); 
     208                TEST_EQUAL("</body>", line); 
     209                TEST_THAT(getline.GetLine(line)); 
     210                TEST_EQUAL("</html>", line); 
     211        } 
    132212         
    133                 // Kill it 
    134                 TEST_THAT(KillServer(pid)); 
    135                 TestRemoteProcessMemLeaks("generic-httpserver.memleaks"); 
    136         } 
     213        // Kill it 
     214        TEST_THAT(KillServer(pid)); 
     215        TestRemoteProcessMemLeaks("generic-httpserver.memleaks"); 
    137216 
    138217        return 0; 
Note: See TracChangeset for help on using the changeset viewer.