~ubuntu-branches/ubuntu/gutsy/virtualbox-ose/gutsy

« back to all changes in this revision

Viewing changes to src/VBox/Frontends/VirtualBox/src/HappyHttp.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-09-08 16:44:58 UTC
  • Revision ID: james.westby@ubuntu.com-20070908164458-wao29470vqtr8ksy
Tags: upstream-1.5.0-dfsg2
ImportĀ upstreamĀ versionĀ 1.5.0-dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** @file
 
2
 *
 
3
 * VBox frontends: Qt GUI ("VirtualBox"):
 
4
 * HappyHTTP library.
 
5
 * Modified for VirtualBox. Original copyright below.
 
6
 */
 
7
 
 
8
/*
 
9
 * Copyright (C) 2006-2007 innotek GmbH
 
10
 *
 
11
 * This file is part of VirtualBox Open Source Edition (OSE), as
 
12
 * available from http://www.virtualbox.org. This file is free software;
 
13
 * you can redistribute it and/or modify it under the terms of the GNU
 
14
 * General Public License as published by the Free Software Foundation,
 
15
 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
 
16
 * distribution. VirtualBox OSE is distributed in the hope that it will
 
17
 * be useful, but WITHOUT ANY WARRANTY of any kind.
 
18
 */
 
19
 
 
20
 
 
21
/*
 
22
 * HappyHTTP - a simple HTTP library
 
23
 * Version 0.1
 
24
 *
 
25
 * Copyright (c) 2006 Ben Campbell
 
26
 *
 
27
 * This software is provided 'as-is', without any express or implied
 
28
 * warranty. In no event will the authors be held liable for any damages
 
29
 * arising from the use of this software.
 
30
 *
 
31
 * Permission is granted to anyone to use this software for any purpose,
 
32
 * including commercial applications, and to alter it and redistribute it
 
33
 * freely, subject to the following restrictions:
 
34
 *
 
35
 * 1. The origin of this software must not be misrepresented; you must not
 
36
 * claim that you wrote the original software. If you use this software in a
 
37
 * product, an acknowledgment in the product documentation would be
 
38
 * appreciated but is not required.
 
39
 *
 
40
 * 2. Altered source versions must be plainly marked as such, and must not
 
41
 * be misrepresented as being the original software.
 
42
 *
 
43
 * 3. This notice may not be removed or altered from any source distribution.
 
44
 *
 
45
 */
 
46
 
 
47
 
 
48
#include "HappyHttp.h"
 
49
 
 
50
#ifndef RT_OS_WINDOWS
 
51
//  #include <sys/types.h>
 
52
    #include <sys/socket.h>
 
53
    #include <netinet/in.h>
 
54
    #include <arpa/inet.h>
 
55
    #include <netdb.h>  // for gethostbyname()
 
56
    #include <errno.h>
 
57
    #include <unistd.h> // close
 
58
#endif
 
59
 
 
60
#ifdef RT_OS_WINDOWS
 
61
    #include <winsock2.h>
 
62
    #define vsnprintf _vsnprintf
 
63
    #define strcasecmp _stricmp
 
64
#endif
 
65
 
 
66
#include <cstdio>
 
67
#include <cstring>
 
68
#include <cstdarg>
 
69
#include <assert.h>
 
70
 
 
71
#include <string>
 
72
#include <vector>
 
73
#include <string>
 
74
#include <algorithm>
 
75
 
 
76
#include <iprt/ctype.h>
 
77
 
 
78
 
 
79
using namespace std;
 
80
 
 
81
 
 
82
namespace happyhttp
 
83
{
 
84
 
 
85
#ifdef RT_OS_WINDOWS
 
86
const char* GetWinsockErrorString( int err );
 
87
#endif
 
88
 
 
89
 
 
90
//---------------------------------------------------------------------
 
91
// Helper functions
 
92
//---------------------------------------------------------------------
 
93
 
 
94
 
 
95
 
 
96
void BailOnSocketError( const char* context )
 
97
{
 
98
#ifdef RT_OS_WINDOWS
 
99
 
 
100
    int e = WSAGetLastError();
 
101
    const char* msg = GetWinsockErrorString( e );
 
102
#else
 
103
    const char* msg = strerror( errno );
 
104
#endif
 
105
    throw Wobbly( "%s: %s", context, msg );
 
106
}
 
107
 
 
108
 
 
109
#ifdef RT_OS_WINDOWS
 
110
 
 
111
const char* GetWinsockErrorString( int err )
 
112
{
 
113
    switch( err)
 
114
    {
 
115
    case 0:                 return "No error";
 
116
    case WSAEINTR:          return "Interrupted system call";
 
117
    case WSAEBADF:          return "Bad file number";
 
118
    case WSAEACCES:         return "Permission denied";
 
119
    case WSAEFAULT:         return "Bad address";
 
120
    case WSAEINVAL:         return "Invalid argument";
 
121
    case WSAEMFILE:         return "Too many open sockets";
 
122
    case WSAEWOULDBLOCK:    return "Operation would block";
 
123
    case WSAEINPROGRESS:    return "Operation now in progress";
 
124
    case WSAEALREADY:       return "Operation already in progress";
 
125
    case WSAENOTSOCK:       return "Socket operation on non-socket";
 
126
    case WSAEDESTADDRREQ:   return "Destination address required";
 
127
    case WSAEMSGSIZE:       return "Message too long";
 
128
    case WSAEPROTOTYPE:     return "Protocol wrong type for socket";
 
129
    case WSAENOPROTOOPT:    return "Bad protocol option";
 
130
    case WSAEPROTONOSUPPORT:    return "Protocol not supported";
 
131
    case WSAESOCKTNOSUPPORT:    return "Socket type not supported";
 
132
    case WSAEOPNOTSUPP:     return "Operation not supported on socket";
 
133
    case WSAEPFNOSUPPORT:   return "Protocol family not supported";
 
134
    case WSAEAFNOSUPPORT:   return "Address family not supported";
 
135
    case WSAEADDRINUSE:     return "Address already in use";
 
136
    case WSAEADDRNOTAVAIL:  return "Can't assign requested address";
 
137
    case WSAENETDOWN:       return "Network is down";
 
138
    case WSAENETUNREACH:    return "Network is unreachable";
 
139
    case WSAENETRESET:      return "Net connection reset";
 
140
    case WSAECONNABORTED:   return "Software caused connection abort";
 
141
    case WSAECONNRESET:     return "Connection reset by peer";
 
142
    case WSAENOBUFS:        return "No buffer space available";
 
143
    case WSAEISCONN:        return "Socket is already connected";
 
144
    case WSAENOTCONN:       return "Socket is not connected";
 
145
    case WSAESHUTDOWN:      return "Can't send after socket shutdown";
 
146
    case WSAETOOMANYREFS:   return "Too many references, can't splice";
 
147
    case WSAETIMEDOUT:      return "Connection timed out";
 
148
    case WSAECONNREFUSED:   return "Connection refused";
 
149
    case WSAELOOP:          return "Too many levels of symbolic links";
 
150
    case WSAENAMETOOLONG:   return "File name too long";
 
151
    case WSAEHOSTDOWN:      return "Host is down";
 
152
    case WSAEHOSTUNREACH:   return "No route to host";
 
153
    case WSAENOTEMPTY:      return "Directory not empty";
 
154
    case WSAEPROCLIM:       return "Too many processes";
 
155
    case WSAEUSERS:         return "Too many users";
 
156
    case WSAEDQUOT:         return "Disc quota exceeded";
 
157
    case WSAESTALE:         return "Stale NFS file handle";
 
158
    case WSAEREMOTE:        return "Too many levels of remote in path";
 
159
    case WSASYSNOTREADY:    return "Network system is unavailable";
 
160
    case WSAVERNOTSUPPORTED:    return "Winsock version out of range";
 
161
    case WSANOTINITIALISED: return "WSAStartup not yet called";
 
162
    case WSAEDISCON:        return "Graceful shutdown in progress";
 
163
    case WSAHOST_NOT_FOUND: return "Host not found";
 
164
    case WSANO_DATA:        return "No host data of that type was found";
 
165
    }
 
166
 
 
167
    return "unknown";
 
168
};
 
169
 
 
170
#endif // RT_OS_WINDOWS
 
171
 
 
172
 
 
173
// return true if socket has data waiting to be read
 
174
bool datawaiting( int sock )
 
175
{
 
176
    fd_set fds;
 
177
    FD_ZERO( &fds );
 
178
    FD_SET( sock, &fds );
 
179
 
 
180
    struct timeval tv;
 
181
    tv.tv_sec = 0;
 
182
    tv.tv_usec = 0;
 
183
 
 
184
    int r = select( sock+1, &fds, NULL, NULL, &tv);
 
185
    if (r < 0)
 
186
        BailOnSocketError( "select" );
 
187
 
 
188
    if( FD_ISSET( sock, &fds ) )
 
189
        return true;
 
190
    else
 
191
        return false;
 
192
}
 
193
 
 
194
 
 
195
// Try to work out address from string
 
196
// returns 0 if bad
 
197
struct in_addr *atoaddr( const char* address)
 
198
{
 
199
    struct hostent *host;
 
200
    static struct in_addr saddr;
 
201
 
 
202
    // First try nnn.nnn.nnn.nnn form
 
203
    saddr.s_addr = inet_addr(address);
 
204
    if (saddr.s_addr != INADDR_NONE)
 
205
        return &saddr;
 
206
 
 
207
    host = gethostbyname(address);
 
208
    if( host )
 
209
        return (struct in_addr *) *host->h_addr_list;
 
210
 
 
211
    return 0;
 
212
}
 
213
 
 
214
 
 
215
 
 
216
 
 
217
 
 
218
 
 
219
 
 
220
//---------------------------------------------------------------------
 
221
//
 
222
// Exception class
 
223
//
 
224
//---------------------------------------------------------------------
 
225
 
 
226
 
 
227
Wobbly::Wobbly( const char* fmt, ... )
 
228
{
 
229
    va_list ap;
 
230
    va_start( ap,fmt);
 
231
    int n = vsnprintf( m_Message, MAXLEN, fmt, ap );
 
232
    va_end( ap );
 
233
    if(n==MAXLEN)
 
234
        m_Message[MAXLEN-1] = '\0';
 
235
}
 
236
 
 
237
 
 
238
 
 
239
 
 
240
 
 
241
 
 
242
 
 
243
 
 
244
//---------------------------------------------------------------------
 
245
//
 
246
// Connection
 
247
//
 
248
//---------------------------------------------------------------------
 
249
Connection::Connection( const char* host, int port ) :
 
250
    m_ResponseBeginCB(0),
 
251
    m_ResponseDataCB(0),
 
252
    m_ResponseCompleteCB(0),
 
253
    m_UserData(0),
 
254
    m_State( IDLE ),
 
255
    m_Host( host ),
 
256
    m_Port( port ),
 
257
    m_Sock(-1)
 
258
{
 
259
#ifdef RT_OS_WINDOWS
 
260
    WSADATA wsaData;
 
261
 
 
262
    int ret = WSAStartup(MAKEWORD(1,1), &wsaData);
 
263
#endif
 
264
}
 
265
 
 
266
 
 
267
void Connection::setcallbacks(
 
268
    ResponseBegin_CB begincb,
 
269
    ResponseData_CB datacb,
 
270
    ResponseComplete_CB completecb,
 
271
    void* userdata )
 
272
{
 
273
    m_ResponseBeginCB = begincb;
 
274
    m_ResponseDataCB = datacb;
 
275
    m_ResponseCompleteCB = completecb;
 
276
    m_UserData = userdata;
 
277
}
 
278
 
 
279
 
 
280
void Connection::connect()
 
281
{
 
282
    in_addr* addr = atoaddr( m_Host.c_str() );
 
283
    if( !addr )
 
284
        throw Wobbly( "Invalid network address" );
 
285
 
 
286
    sockaddr_in address;
 
287
    memset( (char*)&address, 0, sizeof(address) );
 
288
    address.sin_family = AF_INET;
 
289
    address.sin_port = htons( m_Port );
 
290
    address.sin_addr.s_addr = addr->s_addr;
 
291
 
 
292
    m_Sock = socket( AF_INET, SOCK_STREAM, 0 );
 
293
    if( m_Sock < 0 )
 
294
        BailOnSocketError( "socket()" );
 
295
 
 
296
//  printf("Connecting to %s on port %d.\n",inet_ntoa(*addr), port);
 
297
 
 
298
    if( ::connect( m_Sock, (sockaddr const*)&address, sizeof(address) ) < 0 )
 
299
        BailOnSocketError( "connect()" );
 
300
}
 
301
 
 
302
 
 
303
void Connection::close()
 
304
{
 
305
#ifdef RT_OS_WINDOWS
 
306
    if( m_Sock >= 0 )
 
307
        ::closesocket( m_Sock );
 
308
#else
 
309
    if( m_Sock >= 0 )
 
310
        ::close( m_Sock );
 
311
#endif
 
312
    m_Sock = -1;
 
313
 
 
314
    // discard any incomplete responses
 
315
    while( !m_Outstanding.empty() )
 
316
    {
 
317
        delete m_Outstanding.front();
 
318
        m_Outstanding.pop_front();
 
319
    }
 
320
}
 
321
 
 
322
 
 
323
Connection::~Connection()
 
324
{
 
325
    close();
 
326
}
 
327
 
 
328
void Connection::request( const char* method,
 
329
    const char* url,
 
330
    const char* headers[],
 
331
    const unsigned char* body,
 
332
    int bodysize )
 
333
{
 
334
 
 
335
    bool gotcontentlength = false;  // already in headers?
 
336
 
 
337
    // check headers for content-length
 
338
    // TODO: check for "Host" and "Accept-Encoding" too
 
339
    // and avoid adding them ourselves in putrequest()
 
340
    if( headers )
 
341
    {
 
342
        const char** h = headers;
 
343
        while( *h )
 
344
        {
 
345
            const char* name = *h++;
 
346
            const char* value = *h++;
 
347
            assert( value != 0 );   // name with no value!
 
348
 
 
349
            if( 0==strcasecmp( name, "content-length" ) )
 
350
                gotcontentlength = true;
 
351
        }
 
352
    }
 
353
 
 
354
    putrequest( method, url );
 
355
 
 
356
    if( body && !gotcontentlength )
 
357
        putheader( "Content-Length", bodysize );
 
358
 
 
359
    if( headers )
 
360
    {
 
361
        const char** h = headers;
 
362
        while( *h )
 
363
        {
 
364
            const char* name = *h++;
 
365
            const char* value = *h++;
 
366
            putheader( name, value );
 
367
        }
 
368
    }
 
369
    endheaders();
 
370
 
 
371
    if( body )
 
372
        send( body, bodysize );
 
373
 
 
374
}
 
375
 
 
376
 
 
377
 
 
378
 
 
379
void Connection::putrequest( const char* method, const char* url )
 
380
{
 
381
    if( m_State != IDLE )
 
382
        throw Wobbly( "Request already issued" );
 
383
 
 
384
    m_State = REQ_STARTED;
 
385
 
 
386
    char req[ 512 ];
 
387
    sprintf( req, "%s %s HTTP/1.1", method, url );
 
388
    m_Buffer.push_back( req );
 
389
 
 
390
    putheader( "Host", m_Host.c_str() );    // required for HTTP1.1
 
391
 
 
392
    // don't want any fancy encodings please
 
393
    putheader("Accept-Encoding", "identity");
 
394
 
 
395
    // Push a new response onto the queue
 
396
    Response *r = new Response( method, *this );
 
397
    m_Outstanding.push_back( r );
 
398
}
 
399
 
 
400
 
 
401
void Connection::putheader( const char* header, const char* value )
 
402
{
 
403
    if( m_State != REQ_STARTED )
 
404
        throw Wobbly( "putheader() failed" );
 
405
    m_Buffer.push_back( string(header) + ": " + string( value ) );
 
406
}
 
407
 
 
408
void Connection::putheader( const char* header, int numericvalue )
 
409
{
 
410
    char buf[32];
 
411
    sprintf( buf, "%d", numericvalue );
 
412
    putheader( header, buf );
 
413
}
 
414
 
 
415
void Connection::endheaders()
 
416
{
 
417
    if( m_State != REQ_STARTED )
 
418
        throw Wobbly( "Cannot send header" );
 
419
    m_State = IDLE;
 
420
 
 
421
    m_Buffer.push_back( "" );
 
422
 
 
423
    string msg;
 
424
    vector< string>::const_iterator it;
 
425
    for( it = m_Buffer.begin(); it != m_Buffer.end(); ++it )
 
426
        msg += (*it) + "\r\n";
 
427
 
 
428
    m_Buffer.clear();
 
429
 
 
430
//  printf( "%s", msg.c_str() );
 
431
    send( (const unsigned char*)msg.c_str(), msg.size() );
 
432
}
 
433
 
 
434
 
 
435
 
 
436
void Connection::send( const unsigned char* buf, int numbytes )
 
437
{
 
438
//  fwrite( buf, 1,numbytes, stdout );
 
439
 
 
440
    if( m_Sock < 0 )
 
441
        connect();
 
442
 
 
443
    while( numbytes > 0 )
 
444
    {
 
445
#ifdef RT_OS_WINDOWS
 
446
        int n = ::send( m_Sock, (const char*)buf, numbytes, 0 );
 
447
#else
 
448
        int n = ::send( m_Sock, buf, numbytes, 0 );
 
449
#endif
 
450
        if( n<0 )
 
451
            BailOnSocketError( "send()" );
 
452
        numbytes -= n;
 
453
        buf += n;
 
454
    }
 
455
}
 
456
 
 
457
 
 
458
void Connection::pump()
 
459
{
 
460
    if( m_Outstanding.empty() )
 
461
        return;     // no requests outstanding
 
462
 
 
463
    assert( m_Sock >0 );    // outstanding requests but no connection!
 
464
 
 
465
    if( !datawaiting( m_Sock ) )
 
466
        return;             // recv will block
 
467
 
 
468
    unsigned char buf[ 2048 ];
 
469
    int a = recv( m_Sock, (char*)buf, sizeof(buf), 0 );
 
470
    if( a<0 )
 
471
        BailOnSocketError( "recv()" );
 
472
 
 
473
    if( a== 0 )
 
474
    {
 
475
        // connection has closed
 
476
 
 
477
        Response* r = m_Outstanding.front();
 
478
        r->notifyconnectionclosed();
 
479
        assert( r->completed() );
 
480
        delete r;
 
481
        m_Outstanding.pop_front();
 
482
 
 
483
        // any outstanding requests will be discarded
 
484
        close();
 
485
    }
 
486
    else
 
487
    {
 
488
        int used = 0;
 
489
        while( used < a && !m_Outstanding.empty() )
 
490
        {
 
491
 
 
492
            Response* r = m_Outstanding.front();
 
493
            int u = r->pump( &buf[used], a-used );
 
494
 
 
495
            // delete response once completed
 
496
            if( r->completed() )
 
497
            {
 
498
                delete r;
 
499
                m_Outstanding.pop_front();
 
500
            }
 
501
            used += u;
 
502
        }
 
503
 
 
504
        // NOTE: will lose bytes if response queue goes empty
 
505
        // (but server shouldn't be sending anything if we don't have
 
506
        // anything outstanding anyway)
 
507
        assert( used == a );    // all bytes should be used up by here.
 
508
    }
 
509
}
 
510
 
 
511
 
 
512
 
 
513
 
 
514
 
 
515
 
 
516
//---------------------------------------------------------------------
 
517
//
 
518
// Response
 
519
//
 
520
//---------------------------------------------------------------------
 
521
 
 
522
 
 
523
Response::Response( const char* method, Connection& conn ) :
 
524
    m_State( STATUSLINE ),
 
525
    m_Connection( conn ),
 
526
    m_Method( method ),
 
527
    m_Version( 0 ),
 
528
    m_Status(0),
 
529
    m_BytesRead(0),
 
530
    m_Chunked(false),
 
531
    m_ChunkLeft(0),
 
532
    m_Length(-1),
 
533
    m_WillClose(false)
 
534
{
 
535
}
 
536
 
 
537
 
 
538
const char* Response::getheader( const char* name ) const
 
539
{
 
540
    std::string lname( name );
 
541
    std::transform( lname.begin(), lname.end(), lname.begin(), tolower );
 
542
 
 
543
    std::map< std::string, std::string >::const_iterator it = m_Headers.find( lname );
 
544
    if( it == m_Headers.end() )
 
545
        return 0;
 
546
    else
 
547
        return it->second.c_str();
 
548
}
 
549
 
 
550
 
 
551
int Response::getstatus() const
 
552
{
 
553
    // only valid once we've got the statusline
 
554
    assert( m_State != STATUSLINE );
 
555
    return m_Status;
 
556
}
 
557
 
 
558
 
 
559
const char* Response::getreason() const
 
560
{
 
561
    // only valid once we've got the statusline
 
562
    assert( m_State != STATUSLINE );
 
563
    return m_Reason.c_str();
 
564
}
 
565
 
 
566
 
 
567
 
 
568
// Connection has closed
 
569
void Response::notifyconnectionclosed()
 
570
{
 
571
    if( m_State == COMPLETE )
 
572
        return;
 
573
 
 
574
    // eof can be valid...
 
575
    if( m_State == BODY &&
 
576
        !m_Chunked &&
 
577
        m_Length == -1 )
 
578
    {
 
579
        Finish();   // we're all done!
 
580
    }
 
581
    else
 
582
    {
 
583
        throw Wobbly( "Connection closed unexpectedly" );
 
584
    }
 
585
}
 
586
 
 
587
 
 
588
 
 
589
int Response::pump( const unsigned char* data, int datasize )
 
590
{
 
591
    assert( datasize != 0 );
 
592
    int count = datasize;
 
593
 
 
594
    while( count > 0 && m_State != COMPLETE )
 
595
    {
 
596
        if( m_State == STATUSLINE ||
 
597
            m_State == HEADERS ||
 
598
            m_State == TRAILERS ||
 
599
            m_State == CHUNKLEN ||
 
600
            m_State == CHUNKEND )
 
601
        {
 
602
            // we want to accumulate a line
 
603
            while( count > 0 )
 
604
            {
 
605
                char c = (char)*data++;
 
606
                --count;
 
607
                if( c == '\n' )
 
608
                {
 
609
                    // now got a whole line!
 
610
                    switch( m_State )
 
611
                    {
 
612
                        case STATUSLINE:
 
613
                            ProcessStatusLine( m_LineBuf );
 
614
                            break;
 
615
                        case HEADERS:
 
616
                            ProcessHeaderLine( m_LineBuf );
 
617
                            break;
 
618
                        case TRAILERS:
 
619
                            ProcessTrailerLine( m_LineBuf );
 
620
                            break;
 
621
                        case CHUNKLEN:
 
622
                            ProcessChunkLenLine( m_LineBuf );
 
623
                            break;
 
624
                        case CHUNKEND:
 
625
                            // just soak up the crlf after body and go to next state
 
626
                            assert( m_Chunked == true );
 
627
                            m_State = CHUNKLEN;
 
628
                            break;
 
629
                        default:
 
630
                            break;
 
631
                    }
 
632
                    m_LineBuf.clear();
 
633
                    break;      // break out of line accumulation!
 
634
                }
 
635
                else
 
636
                {
 
637
                    if( c != '\r' )     // just ignore CR
 
638
                        m_LineBuf += c;
 
639
                }
 
640
            }
 
641
        }
 
642
        else if( m_State == BODY )
 
643
        {
 
644
            int bytesused = 0;
 
645
            if( m_Chunked )
 
646
                bytesused = ProcessDataChunked( data, count );
 
647
            else
 
648
                bytesused = ProcessDataNonChunked( data, count );
 
649
            data += bytesused;
 
650
            count -= bytesused;
 
651
        }
 
652
    }
 
653
 
 
654
    // return number of bytes used
 
655
    return datasize - count;
 
656
}
 
657
 
 
658
 
 
659
 
 
660
void Response::ProcessChunkLenLine( std::string const& line )
 
661
{
 
662
    // chunklen in hex at beginning of line
 
663
    m_ChunkLeft = strtol( line.c_str(), NULL, 16 );
 
664
 
 
665
    if( m_ChunkLeft == 0 )
 
666
    {
 
667
        // got the whole body, now check for trailing headers
 
668
        m_State = TRAILERS;
 
669
        m_HeaderAccum.clear();
 
670
    }
 
671
    else
 
672
    {
 
673
        m_State = BODY;
 
674
    }
 
675
}
 
676
 
 
677
 
 
678
// handle some body data in chunked mode
 
679
// returns number of bytes used.
 
680
int Response::ProcessDataChunked( const unsigned char* data, int count )
 
681
{
 
682
    assert( m_Chunked );
 
683
 
 
684
    int n = count;
 
685
    if( n>m_ChunkLeft )
 
686
        n = m_ChunkLeft;
 
687
 
 
688
    // invoke callback to pass out the data
 
689
    if( m_Connection.m_ResponseDataCB )
 
690
        (m_Connection.m_ResponseDataCB)( this, m_Connection.m_UserData, data, n );
 
691
 
 
692
    m_BytesRead += n;
 
693
 
 
694
    m_ChunkLeft -= n;
 
695
    assert( m_ChunkLeft >= 0);
 
696
    if( m_ChunkLeft == 0 )
 
697
    {
 
698
        // chunk completed! now soak up the trailing CRLF before next chunk
 
699
        m_State = CHUNKEND;
 
700
    }
 
701
    return n;
 
702
}
 
703
 
 
704
// handle some body data in non-chunked mode.
 
705
// returns number of bytes used.
 
706
int Response::ProcessDataNonChunked( const unsigned char* data, int count )
 
707
{
 
708
    int n = count;
 
709
    if( m_Length != -1 )
 
710
    {
 
711
        // we know how many bytes to expect
 
712
        int remaining = m_Length - m_BytesRead;
 
713
        if( n > remaining )
 
714
            n = remaining;
 
715
    }
 
716
 
 
717
    // invoke callback to pass out the data
 
718
    if( m_Connection.m_ResponseDataCB )
 
719
        (m_Connection.m_ResponseDataCB)( this, m_Connection.m_UserData, data, n );
 
720
 
 
721
    m_BytesRead += n;
 
722
 
 
723
    // Finish if we know we're done. Else we're waiting for connection close.
 
724
    if( m_Length != -1 && m_BytesRead == m_Length )
 
725
        Finish();
 
726
 
 
727
    return n;
 
728
}
 
729
 
 
730
 
 
731
void Response::Finish()
 
732
{
 
733
    m_State = COMPLETE;
 
734
 
 
735
    // invoke the callback
 
736
    if( m_Connection.m_ResponseCompleteCB )
 
737
        (m_Connection.m_ResponseCompleteCB)( this, m_Connection.m_UserData );
 
738
}
 
739
 
 
740
 
 
741
void Response::ProcessStatusLine( std::string const& line )
 
742
{
 
743
    const char* p = line.c_str();
 
744
 
 
745
    // skip any leading space
 
746
    while( *p && *p == ' ' )
 
747
        ++p;
 
748
 
 
749
    // get version
 
750
    while( *p && *p != ' ' )
 
751
        m_VersionString += *p++;
 
752
    while( *p && *p == ' ' )
 
753
        ++p;
 
754
 
 
755
    // get status code
 
756
    std::string status;
 
757
    while( *p && *p != ' ' )
 
758
        status += *p++;
 
759
    while( *p && *p == ' ' )
 
760
        ++p;
 
761
 
 
762
    // rest of line is reason
 
763
    while( *p )
 
764
        m_Reason += *p++;
 
765
 
 
766
    m_Status = atoi( status.c_str() );
 
767
    if( m_Status < 100 || m_Status > 999 )
 
768
        throw Wobbly( "BadStatusLine (%s)", line.c_str() );
 
769
 
 
770
/*
 
771
    printf( "version: '%s'\n", m_VersionString.c_str() );
 
772
    printf( "status: '%d'\n", m_Status );
 
773
    printf( "reason: '%s'\n", m_Reason.c_str() );
 
774
*/
 
775
 
 
776
    if( m_VersionString == "HTTP:/1.0" )
 
777
        m_Version = 10;
 
778
    else if( 0==m_VersionString.compare( 0,7,"HTTP/1." ) )
 
779
        m_Version = 11;
 
780
    else
 
781
        throw Wobbly( "UnknownProtocol (%s)", m_VersionString.c_str() );
 
782
    // TODO: support for HTTP/0.9
 
783
 
 
784
 
 
785
    // OK, now we expect headers!
 
786
    m_State = HEADERS;
 
787
    m_HeaderAccum.clear();
 
788
}
 
789
 
 
790
 
 
791
// process accumulated header data
 
792
void Response::FlushHeader()
 
793
{
 
794
    if( m_HeaderAccum.empty() )
 
795
        return; // no flushing required
 
796
 
 
797
    const char* p = m_HeaderAccum.c_str();
 
798
 
 
799
    std::string header;
 
800
    std::string value;
 
801
    while( *p && *p != ':' )
 
802
        header += tolower( *p++ );
 
803
 
 
804
    // skip ':'
 
805
    if( *p )
 
806
        ++p;
 
807
 
 
808
    // skip space
 
809
    while( *p && (*p ==' ' || *p=='\t') )
 
810
        ++p;
 
811
 
 
812
    value = p; // rest of line is value
 
813
 
 
814
    m_Headers[ header ] = value;
 
815
//  printf("header: ['%s': '%s']\n", header.c_str(), value.c_str() );
 
816
 
 
817
    m_HeaderAccum.clear();
 
818
}
 
819
 
 
820
 
 
821
void Response::ProcessHeaderLine( std::string const& line )
 
822
{
 
823
    const char* p = line.c_str();
 
824
    if( line.empty() )
 
825
    {
 
826
        FlushHeader();
 
827
        // end of headers
 
828
 
 
829
        // HTTP code 100 handling (we ignore 'em)
 
830
        if( m_Status == CONTINUE )
 
831
            m_State = STATUSLINE;   // reset parsing, expect new status line
 
832
        else
 
833
            BeginBody();            // start on body now!
 
834
        return;
 
835
    }
 
836
 
 
837
    if( isspace(*p) )
 
838
    {
 
839
        // it's a continuation line - just add it to previous data
 
840
        ++p;
 
841
        while( *p && isspace( *p ) )
 
842
            ++p;
 
843
 
 
844
        m_HeaderAccum += ' ';
 
845
        m_HeaderAccum += p;
 
846
    }
 
847
    else
 
848
    {
 
849
        // begin a new header
 
850
        FlushHeader();
 
851
        m_HeaderAccum = p;
 
852
    }
 
853
}
 
854
 
 
855
 
 
856
void Response::ProcessTrailerLine( std::string const& line )
 
857
{
 
858
    // TODO: handle trailers?
 
859
    // (python httplib doesn't seem to!)
 
860
    if( line.empty() )
 
861
        Finish();
 
862
 
 
863
    // just ignore all the trailers...
 
864
}
 
865
 
 
866
 
 
867
 
 
868
// OK, we've now got all the headers read in, so we're ready to start
 
869
// on the body. But we need to see what info we can glean from the headers
 
870
// first...
 
871
void Response::BeginBody()
 
872
{
 
873
 
 
874
    m_Chunked = false;
 
875
    m_Length = -1;  // unknown
 
876
    m_WillClose = false;
 
877
 
 
878
    // using chunked encoding?
 
879
    const char* trenc = getheader( "transfer-encoding" );
 
880
    if( trenc && 0==strcasecmp( trenc, "chunked") )
 
881
    {
 
882
        m_Chunked = true;
 
883
        m_ChunkLeft = -1;   // unknown
 
884
    }
 
885
 
 
886
    m_WillClose = CheckClose();
 
887
 
 
888
    // length supplied?
 
889
    const char* contentlen = getheader( "content-length" );
 
890
    if( contentlen && !m_Chunked )
 
891
    {
 
892
        m_Length = atoi( contentlen );
 
893
    }
 
894
 
 
895
    // check for various cases where we expect zero-length body
 
896
    if( m_Status == NO_CONTENT ||
 
897
        m_Status == NOT_MODIFIED ||
 
898
        ( m_Status >= 100 && m_Status < 200 ) ||        // 1xx codes have no body
 
899
        m_Method == "HEAD" )
 
900
    {
 
901
        m_Length = 0;
 
902
    }
 
903
 
 
904
 
 
905
    // if we're not using chunked mode, and no length has been specified,
 
906
    // assume connection will close at end.
 
907
    if( !m_WillClose && !m_Chunked && m_Length == -1 )
 
908
        m_WillClose = true;
 
909
 
 
910
 
 
911
 
 
912
    // Invoke the user callback, if any
 
913
    if( m_Connection.m_ResponseBeginCB )
 
914
        (m_Connection.m_ResponseBeginCB)( this, m_Connection.m_UserData );
 
915
 
 
916
/*
 
917
    printf("---------BeginBody()--------\n");
 
918
    printf("Length: %d\n", m_Length );
 
919
    printf("WillClose: %d\n", (int)m_WillClose );
 
920
    printf("Chunked: %d\n", (int)m_Chunked );
 
921
    printf("ChunkLeft: %d\n", (int)m_ChunkLeft );
 
922
    printf("----------------------------\n");
 
923
*/
 
924
    // now start reading body data!
 
925
    if( m_Chunked )
 
926
        m_State = CHUNKLEN;
 
927
    else
 
928
        m_State = BODY;
 
929
}
 
930
 
 
931
 
 
932
// return true if we think server will automatically close connection
 
933
bool Response::CheckClose()
 
934
{
 
935
    if( m_Version == 11 )
 
936
    {
 
937
        // HTTP1.1
 
938
        // the connection stays open unless "connection: close" is specified.
 
939
        const char* conn = getheader( "connection" );
 
940
        if( conn && 0==strcasecmp( conn, "close" ) )
 
941
            return true;
 
942
        else
 
943
            return false;
 
944
    }
 
945
 
 
946
    // Older HTTP
 
947
    // keep-alive header indicates persistant connection
 
948
    if( getheader( "keep-alive" ) )
 
949
        return false;
 
950
 
 
951
    // TODO: some special case handling for Akamai and netscape maybe?
 
952
    // (see _check_close() in python httplib.py for details)
 
953
 
 
954
    return true;
 
955
}
 
956
 
 
957
 
 
958
 
 
959
}   // end namespace happyhttp
 
960
 
 
961