737e76a69f 2009-03-30 drh: /* 737e76a69f 2009-03-30 drh: ** Copyright (c) 2009 D. Richard Hipp 737e76a69f 2009-03-30 drh: ** 737e76a69f 2009-03-30 drh: ** This program is free software; you can redistribute it and/or 737e76a69f 2009-03-30 drh: ** modify it under the terms of the GNU General Public 737e76a69f 2009-03-30 drh: ** License version 2 as published by the Free Software Foundation. 737e76a69f 2009-03-30 drh: ** 737e76a69f 2009-03-30 drh: ** This program is distributed in the hope that it will be useful, 737e76a69f 2009-03-30 drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of 737e76a69f 2009-03-30 drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 737e76a69f 2009-03-30 drh: ** General Public License for more details. 737e76a69f 2009-03-30 drh: ** 737e76a69f 2009-03-30 drh: ** You should have received a copy of the GNU General Public 737e76a69f 2009-03-30 drh: ** License along with this library; if not, write to the 737e76a69f 2009-03-30 drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, 737e76a69f 2009-03-30 drh: ** Boston, MA 02111-1307, USA. 737e76a69f 2009-03-30 drh: ** 737e76a69f 2009-03-30 drh: ** Author contact information: 737e76a69f 2009-03-30 drh: ** drh@hwaci.com 737e76a69f 2009-03-30 drh: ** http://www.hwaci.com/drh/ 737e76a69f 2009-03-30 drh: ** 737e76a69f 2009-03-30 drh: ******************************************************************************* 737e76a69f 2009-03-30 drh: ** 737e76a69f 2009-03-30 drh: ** This module implements the transport layer for the client side HTTP 737e76a69f 2009-03-30 drh: ** connection. The purpose of this layer is to provide a common interface 737e76a69f 2009-03-30 drh: ** for both HTTP and HTTPS and to provide a common "fetch one line" 737e76a69f 2009-03-30 drh: ** interface that is used for parsing the reply. 737e76a69f 2009-03-30 drh: */ 737e76a69f 2009-03-30 drh: #include "config.h" 737e76a69f 2009-03-30 drh: #include "http_transport.h" 737e76a69f 2009-03-30 drh: 737e76a69f 2009-03-30 drh: /* 737e76a69f 2009-03-30 drh: ** State information 737e76a69f 2009-03-30 drh: */ 737e76a69f 2009-03-30 drh: static struct { 945ecd1a8b 2009-04-11 drh: int isOpen; /* True when the transport layer is open */ 945ecd1a8b 2009-04-11 drh: char *pBuf; /* Buffer used to hold the reply */ 945ecd1a8b 2009-04-11 drh: int nAlloc; /* Space allocated for transportBuf[] */ 945ecd1a8b 2009-04-11 drh: int nUsed ; /* Space of transportBuf[] used */ 945ecd1a8b 2009-04-11 drh: int iCursor; /* Next unread by in transportBuf[] */ 945ecd1a8b 2009-04-11 drh: FILE *pFile; /* File I/O for FILE: */ 945ecd1a8b 2009-04-11 drh: char *zOutFile; /* Name of outbound file for FILE: */ 945ecd1a8b 2009-04-11 drh: char *zInFile; /* Name of inbound file for FILE: */ 737e76a69f 2009-03-30 drh: } transport = { 327823e39b 2009-03-30 drh: 0, 0, 0, 0, 0 737e76a69f 2009-03-30 drh: }; 737e76a69f 2009-03-30 drh: 737e76a69f 2009-03-30 drh: /* 737e76a69f 2009-03-30 drh: ** Return the current transport error message. 737e76a69f 2009-03-30 drh: */ 737e76a69f 2009-03-30 drh: const char *transport_errmsg(void){ 737e76a69f 2009-03-30 drh: return socket_errmsg(); 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: 737e76a69f 2009-03-30 drh: /* 737e76a69f 2009-03-30 drh: ** Open a connection to the server. The server is defined by the following 737e76a69f 2009-03-30 drh: ** global variables: 737e76a69f 2009-03-30 drh: ** 737e76a69f 2009-03-30 drh: ** g.urlName Name of the server. Ex: www.fossil-scm.org 737e76a69f 2009-03-30 drh: ** g.urlPort TCP/IP port. Ex: 80 737e76a69f 2009-03-30 drh: ** g.urlIsHttps Use TLS for the connection 737e76a69f 2009-03-30 drh: ** 737e76a69f 2009-03-30 drh: ** Return the number of errors. 737e76a69f 2009-03-30 drh: */ 737e76a69f 2009-03-30 drh: int transport_open(void){ 737e76a69f 2009-03-30 drh: int rc = 0; 327823e39b 2009-03-30 drh: if( transport.isOpen==0 ){ 327823e39b 2009-03-30 drh: if( g.urlIsHttps ){ 327823e39b 2009-03-30 drh: socket_set_errmsg("HTTPS: is not yet implemented"); 327823e39b 2009-03-30 drh: rc = 1; 327823e39b 2009-03-30 drh: }else if( g.urlIsFile ){ 945ecd1a8b 2009-04-11 drh: sqlite3_uint64 iRandId; 945ecd1a8b 2009-04-11 drh: sqlite3_randomness(sizeof(iRandId), &iRandId); 945ecd1a8b 2009-04-11 drh: transport.zOutFile = mprintf("%s-%llu-out.http", 945ecd1a8b 2009-04-11 drh: g.zRepositoryName, iRandId); 945ecd1a8b 2009-04-11 drh: transport.zInFile = mprintf("%s-%llu-in.http", 945ecd1a8b 2009-04-11 drh: g.zRepositoryName, iRandId); 945ecd1a8b 2009-04-11 drh: transport.pFile = fopen(transport.zOutFile, "wb"); 945ecd1a8b 2009-04-11 drh: if( transport.pFile==0 ){ 945ecd1a8b 2009-04-11 drh: fossil_fatal("cannot output temporary file: %s", transport.zOutFile); 945ecd1a8b 2009-04-11 drh: } 945ecd1a8b 2009-04-11 drh: transport.isOpen = 1; 327823e39b 2009-03-30 drh: }else{ 327823e39b 2009-03-30 drh: rc = socket_open(); 327823e39b 2009-03-30 drh: if( rc==0 ) transport.isOpen = 1; 327823e39b 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: return rc; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: 737e76a69f 2009-03-30 drh: /* 737e76a69f 2009-03-30 drh: ** Close the current connection 737e76a69f 2009-03-30 drh: */ 737e76a69f 2009-03-30 drh: void transport_close(void){ 737e76a69f 2009-03-30 drh: if( transport.isOpen ){ 737e76a69f 2009-03-30 drh: free(transport.pBuf); 737e76a69f 2009-03-30 drh: transport.pBuf = 0; 737e76a69f 2009-03-30 drh: transport.nAlloc = 0; 737e76a69f 2009-03-30 drh: transport.nUsed = 0; 737e76a69f 2009-03-30 drh: transport.iCursor = 0; 327823e39b 2009-03-30 drh: if( g.urlIsHttps ){ 327823e39b 2009-03-30 drh: /* TBD */ 327823e39b 2009-03-30 drh: }else if( g.urlIsFile ){ 945ecd1a8b 2009-04-11 drh: if( transport.pFile ){ 945ecd1a8b 2009-04-11 drh: fclose(transport.pFile); 945ecd1a8b 2009-04-11 drh: transport.pFile = 0; 945ecd1a8b 2009-04-11 drh: } 945ecd1a8b 2009-04-11 drh: unlink(transport.zInFile); 945ecd1a8b 2009-04-11 drh: unlink(transport.zOutFile); a742cfa292 2009-04-11 drh: free(transport.zInFile); 945ecd1a8b 2009-04-11 drh: free(transport.zOutFile); 327823e39b 2009-03-30 drh: }else{ 327823e39b 2009-03-30 drh: socket_close(); 327823e39b 2009-03-30 drh: } 327823e39b 2009-03-30 drh: transport.isOpen = 0; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: 737e76a69f 2009-03-30 drh: /* 737e76a69f 2009-03-30 drh: ** Send content over the wire. 737e76a69f 2009-03-30 drh: */ 737e76a69f 2009-03-30 drh: void transport_send(Blob *toSend){ 737e76a69f 2009-03-30 drh: char *z = blob_buffer(toSend); 737e76a69f 2009-03-30 drh: int n = blob_size(toSend); 327823e39b 2009-03-30 drh: if( g.urlIsHttps ){ 327823e39b 2009-03-30 drh: /* TBD */ 327823e39b 2009-03-30 drh: }else if( g.urlIsFile ){ 945ecd1a8b 2009-04-11 drh: fwrite(z, 1, n, transport.pFile); 945ecd1a8b 2009-04-11 drh: }else{ 327823e39b 2009-03-30 drh: int sent; 327823e39b 2009-03-30 drh: while( n>0 ){ 327823e39b 2009-03-30 drh: sent = socket_send(0, z, n); 327823e39b 2009-03-30 drh: if( sent<=0 ) break; 327823e39b 2009-03-30 drh: n -= sent; 327823e39b 2009-03-30 drh: } 63ef585508 2009-03-30 drh: } 63ef585508 2009-03-30 drh: } 63ef585508 2009-03-30 drh: 63ef585508 2009-03-30 drh: /* 63ef585508 2009-03-30 drh: ** This routine is called when the outbound message is complete and 63ef585508 2009-03-30 drh: ** it is time to being recieving a reply. 63ef585508 2009-03-30 drh: */ 63ef585508 2009-03-30 drh: void transport_flip(void){ 63ef585508 2009-03-30 drh: if( g.urlIsFile ){ 945ecd1a8b 2009-04-11 drh: char *zCmd; 945ecd1a8b 2009-04-11 drh: fclose(transport.pFile); 945ecd1a8b 2009-04-11 drh: zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1", a742cfa292 2009-04-11 drh: g.argv[0], g.urlName, transport.zOutFile, transport.zInFile 945ecd1a8b 2009-04-11 drh: ); 945ecd1a8b 2009-04-11 drh: system(zCmd); 945ecd1a8b 2009-04-11 drh: free(zCmd); 945ecd1a8b 2009-04-11 drh: transport.pFile = fopen(transport.zInFile, "rb"); 63ef585508 2009-03-30 drh: } 63ef585508 2009-03-30 drh: } 63ef585508 2009-03-30 drh: 63ef585508 2009-03-30 drh: /* 63ef585508 2009-03-30 drh: ** This routine is called when the inbound message has been received 63ef585508 2009-03-30 drh: ** and it is time to start sending again. 63ef585508 2009-03-30 drh: */ 63ef585508 2009-03-30 drh: void transport_rewind(void){ 63ef585508 2009-03-30 drh: if( g.urlIsFile ){ 63ef585508 2009-03-30 drh: transport_close(); 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: 737e76a69f 2009-03-30 drh: /* 737e76a69f 2009-03-30 drh: ** Read N bytes of content from the wire and store in the supplied buffer. 737e76a69f 2009-03-30 drh: ** Return the number of bytes actually received. 737e76a69f 2009-03-30 drh: */ 737e76a69f 2009-03-30 drh: int transport_receive(char *zBuf, int N){ 737e76a69f 2009-03-30 drh: int onHand; /* Bytes current held in the transport buffer */ 737e76a69f 2009-03-30 drh: int nByte = 0; /* Bytes of content received */ 737e76a69f 2009-03-30 drh: 737e76a69f 2009-03-30 drh: onHand = transport.nUsed - transport.iCursor; 737e76a69f 2009-03-30 drh: if( onHand>0 ){ 737e76a69f 2009-03-30 drh: int toMove = onHand; 737e76a69f 2009-03-30 drh: if( toMove>N ) toMove = N; 737e76a69f 2009-03-30 drh: memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); 737e76a69f 2009-03-30 drh: transport.iCursor += toMove; 737e76a69f 2009-03-30 drh: if( transport.iCursor>=transport.nUsed ){ 737e76a69f 2009-03-30 drh: transport.nUsed = 0; 737e76a69f 2009-03-30 drh: transport.iCursor = 0; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: N -= toMove; 737e76a69f 2009-03-30 drh: zBuf += toMove; 737e76a69f 2009-03-30 drh: nByte += toMove; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: if( N>0 ){ 327823e39b 2009-03-30 drh: int got; 327823e39b 2009-03-30 drh: if( g.urlIsHttps ){ 327823e39b 2009-03-30 drh: /* TBD */ 327823e39b 2009-03-30 drh: got = 0; 327823e39b 2009-03-30 drh: }else if( g.urlIsFile ){ a742cfa292 2009-04-11 drh: got = fread(zBuf, 1, N, transport.pFile); 327823e39b 2009-03-30 drh: }else{ 327823e39b 2009-03-30 drh: got = socket_receive(0, zBuf, N); 327823e39b 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: if( got>0 ){ 737e76a69f 2009-03-30 drh: nByte += got; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: return nByte; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: 737e76a69f 2009-03-30 drh: /* 737e76a69f 2009-03-30 drh: ** Load up to N new bytes of content into the transport.pBuf buffer. 737e76a69f 2009-03-30 drh: ** The buffer itself might be moved. And the transport.iCursor value 737e76a69f 2009-03-30 drh: ** might be reset to 0. 737e76a69f 2009-03-30 drh: */ 737e76a69f 2009-03-30 drh: static void transport_load_buffer(int N){ 737e76a69f 2009-03-30 drh: int i, j; 737e76a69f 2009-03-30 drh: if( transport.nAlloc==0 ){ 737e76a69f 2009-03-30 drh: transport.nAlloc = N; 737e76a69f 2009-03-30 drh: transport.pBuf = malloc( N ); 737e76a69f 2009-03-30 drh: if( transport.pBuf==0 ) fossil_panic("out of memory"); 737e76a69f 2009-03-30 drh: transport.iCursor = 0; 737e76a69f 2009-03-30 drh: transport.nUsed = 0; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: if( transport.iCursor>0 ){ 737e76a69f 2009-03-30 drh: for(i=0, j=transport.iCursor; j<transport.nUsed; i++, j++){ 737e76a69f 2009-03-30 drh: transport.pBuf[i] = transport.pBuf[j]; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: transport.nUsed -= transport.iCursor; 737e76a69f 2009-03-30 drh: transport.iCursor = 0; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: if( transport.nUsed + N > transport.nAlloc ){ 737e76a69f 2009-03-30 drh: char *pNew; 737e76a69f 2009-03-30 drh: transport.nAlloc = transport.nUsed + N; 737e76a69f 2009-03-30 drh: pNew = realloc(transport.pBuf, transport.nAlloc); 737e76a69f 2009-03-30 drh: if( pNew==0 ) fossil_panic("out of memory"); 737e76a69f 2009-03-30 drh: transport.pBuf = pNew; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: if( N>0 ){ 737e76a69f 2009-03-30 drh: i = transport_receive(&transport.pBuf[transport.nUsed], N); 737e76a69f 2009-03-30 drh: if( i>0 ){ 737e76a69f 2009-03-30 drh: transport.nUsed += i; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: 737e76a69f 2009-03-30 drh: /* 737e76a69f 2009-03-30 drh: ** Fetch a single line of input where a line is all text up to the next 737e76a69f 2009-03-30 drh: ** \n character or until the end of input. Remove all trailing whitespace 737e76a69f 2009-03-30 drh: ** from the received line and zero-terminate the result. Return a pointer 737e76a69f 2009-03-30 drh: ** to the line. 737e76a69f 2009-03-30 drh: ** 737e76a69f 2009-03-30 drh: ** Each call to this routine potentially overwrites the returned buffer. 737e76a69f 2009-03-30 drh: */ 737e76a69f 2009-03-30 drh: char *transport_receive_line(void){ 737e76a69f 2009-03-30 drh: int i; 737e76a69f 2009-03-30 drh: int iStart; 737e76a69f 2009-03-30 drh: 737e76a69f 2009-03-30 drh: i = iStart = transport.iCursor; 737e76a69f 2009-03-30 drh: while(1){ 737e76a69f 2009-03-30 drh: if( i >= transport.nUsed ){ 737e76a69f 2009-03-30 drh: transport_load_buffer(1000); 737e76a69f 2009-03-30 drh: i -= iStart; 737e76a69f 2009-03-30 drh: iStart = 0; 737e76a69f 2009-03-30 drh: if( i >= transport.nUsed ){ 737e76a69f 2009-03-30 drh: transport.pBuf[i] = 0; 737e76a69f 2009-03-30 drh: transport.iCursor = i; 737e76a69f 2009-03-30 drh: break; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: if( transport.pBuf[i]=='\n' ){ 737e76a69f 2009-03-30 drh: transport.iCursor = i+1; 737e76a69f 2009-03-30 drh: while( i>=iStart && isspace(transport.pBuf[i]) ){ 737e76a69f 2009-03-30 drh: transport.pBuf[i] = 0; 737e76a69f 2009-03-30 drh: i--; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: break; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: i++; 737e76a69f 2009-03-30 drh: } 737e76a69f 2009-03-30 drh: return &transport.pBuf[iStart]; 737e76a69f 2009-03-30 drh: }