1 /************************************************************************/
2 /*									*/
3 /*  sioHttp.[ch]:	Retrieve an URL.				*/
4 /*									*/
5 /************************************************************************/
6 
7 #   include	"appUtilConfig.h"
8 
9 #   include     <string.h>
10 #   include     <stdlib.h>
11 
12 #   include     <errno.h>
13 #   include     <unistd.h>
14 #   include     <sys/socket.h>
15 
16 #   include     "sioHttp.h"
17 #   include	"utilMemoryBuffer.h"
18 
19 #   include     <appDebugon.h>
20 
21 typedef enum CommunicationPhase
22     {
23     CP_Unknown= 0,
24     CP_WriteHeader,
25     CP_WriteBody,
26     CP_ReadHeader,
27     CP_ReadBody,
28 
29     CP__COUNT
30     } CommunicationPhase;
31 
32 typedef struct HttpConnection
33     {
34     int			hcFd;
35     int			hcCommunicationPhase;
36     unsigned char	hcPhases[CP__COUNT];
37 
38     MemoryBuffer	hcRequestHeaders;
39     MemoryBuffer	hcRequestBody;
40 
41     unsigned char	hcHeaderBuffer[SIOsizBUF];
42     int			hcInHeaderBuffer;
43     int			hcUseHeaderBuffer;
44     } HttpConnection;
45 
46 #   define	HCphaseUNUSED		0x00
47 #   define	HCphaseUSED		0x01
48 #   define	HCphaseGOT_END		0x02
49 #   define	HCphaseEXHAUSTED	0x04
50 #   define	HCphaseCLOSED		0x08
51 
52 /************************************************************************/
53 /*									*/
54 /*  The output stream. (PUT,POST)					*/
55 /*									*/
56 /************************************************************************/
57 
sioHttpCloseHeaderOutput(void * voidhc)58 static int sioHttpCloseHeaderOutput(       void *  voidhc )
59     {
60     int			rval= 0;
61     HttpConnection *	hc= (HttpConnection *)voidhc;
62 
63     if  ( hc->hcCommunicationPhase == CP_WriteHeader )
64 	{
65 	hc->hcPhases[hc->hcCommunicationPhase++] |= HCphaseCLOSED;
66 	while( hc->hcCommunicationPhase < CP__COUNT			&&
67 	       ! (hc->hcPhases[hc->hcCommunicationPhase] & HCphaseUSED)	)
68 	    { hc->hcCommunicationPhase++;	}
69 	}
70     else{ LDEB(hc->hcCommunicationPhase); rval= -1;	}
71 
72     return rval;
73     }
74 
sioOutHttpWriteHeaderBytes(void * voidhc,const unsigned char * buffer,int count)75 static int sioOutHttpWriteHeaderBytes(	void *			voidhc,
76 					const unsigned char *	buffer,
77 					int			count )
78     {
79     HttpConnection *	hc= (HttpConnection *)voidhc;
80 
81     if  ( hc->hcCommunicationPhase == CP_WriteHeader )
82 	{
83 	if  ( utilMemoryBufferAppendBytes( &(hc->hcRequestHeaders), buffer, count ) )
84 	    { LDEB(count); return -1;	}
85 
86 	return count;
87 	}
88     else{ LDEB(hc->hcCommunicationPhase); return -1;	}
89     }
90 
91 /************************************************************************/
92 /*									*/
93 /*  The output stream. (PUT,POST)					*/
94 /*									*/
95 /************************************************************************/
96 
sioHttpCloseBodyOutput(void * voidhc)97 static int sioHttpCloseBodyOutput(       void *  voidhc )
98     {
99     int			rval= 0;
100     HttpConnection *	hc= (HttpConnection *)voidhc;
101 
102     if  ( hc->hcCommunicationPhase == CP_WriteBody )
103 	{
104 	int		done;
105 
106 	char		scratch[30];
107 	const char *	cl= "Content-Length:";
108 	const char *	crlf= "\r\n";
109 
110 	/******/
111 
112 	sprintf( scratch, "%d", hc->hcRequestBody.mbSize );
113 
114 	if  ( utilMemoryBufferAppendBytes( &(hc->hcRequestHeaders),
115 			(const unsigned char *)cl, strlen( cl ) ) )
116 	    { LDEB(1); return -1;	}
117 
118 	if  ( utilMemoryBufferAppendBytes( &(hc->hcRequestHeaders),
119 			(const unsigned char *)scratch, strlen( scratch ) ) )
120 	    { LDEB(1); return -1;	}
121 
122 	if  ( utilMemoryBufferAppendBytes( &(hc->hcRequestHeaders),
123 			(const unsigned char *)crlf, strlen( crlf ) ) )
124 	    { LDEB(1); return -1;	}
125 
126 	if  ( utilMemoryBufferAppendBytes( &(hc->hcRequestHeaders),
127 			(const unsigned char *)crlf, strlen( crlf ) ) )
128 	    { LDEB(1); return -1;	}
129 
130 	done= write( hc->hcFd, hc->hcRequestHeaders.mbBytes,
131 						hc->hcRequestHeaders.mbSize );
132 	if  ( done != hc->hcRequestHeaders.mbSize )
133 	    { LLDEB(hc->hcRequestHeaders.mbSize,done); return -1;	}
134 
135 	utilCleanMemoryBuffer( &(hc->hcRequestHeaders) );
136 	utilInitMemoryBuffer( &(hc->hcRequestHeaders) );
137 
138 	/******/
139 
140 	done= write( hc->hcFd, hc->hcRequestBody.mbBytes,
141 					    hc->hcRequestBody.mbSize );
142 	if  ( done != hc->hcRequestBody.mbSize )
143 	    { LLDEB(hc->hcRequestBody.mbSize,done); return -1;	}
144 
145 	if  ( shutdown( hc->hcFd, 1 ) )
146 	    { LSDEB(hc->hcFd,strerror(errno)); rval= -1;	}
147 
148 	utilCleanMemoryBuffer( &(hc->hcRequestBody) );
149 	utilInitMemoryBuffer( &(hc->hcRequestBody) );
150 
151 	/******/
152 
153 	hc->hcPhases[hc->hcCommunicationPhase++] |= HCphaseCLOSED;
154 	while( hc->hcCommunicationPhase < CP__COUNT			&&
155 	       ! (hc->hcPhases[hc->hcCommunicationPhase] & HCphaseUSED)	)
156 	    { hc->hcCommunicationPhase++;	}
157 	}
158     else{ LDEB(hc->hcCommunicationPhase); rval= -1;	}
159 
160     return rval;
161     }
162 
sioOutHttpWriteBodyBytes(void * voidhc,const unsigned char * buffer,int count)163 static int sioOutHttpWriteBodyBytes(	void *			voidhc,
164 					const unsigned char *	buffer,
165 					int			count )
166     {
167     HttpConnection *	hc= (HttpConnection *)voidhc;
168 
169     if  ( hc->hcCommunicationPhase == CP_WriteBody )
170 	{
171 	if  ( utilMemoryBufferAppendBytes( &(hc->hcRequestBody), buffer, count ) )
172 	    { LDEB(count); return -1;	}
173 
174 	return count;
175 	}
176     else{ LDEB(hc->hcCommunicationPhase); return -1;	}
177     }
178 
179 /************************************************************************/
180 /*									*/
181 /*  The header stream. (GET,HEAD)					*/
182 /*									*/
183 /************************************************************************/
184 
sioHttpUseHeaderBuffer(HttpConnection * hc,unsigned char * buffer,int count)185 static int sioHttpUseHeaderBuffer(	HttpConnection *	hc,
186 					unsigned char *		buffer,
187 					int			count )
188     {
189     int		use;
190 
191     use= hc->hcUseHeaderBuffer;
192     if  ( use > count )
193 	{ use=  count;	}
194 
195     memcpy( buffer, hc->hcHeaderBuffer, use );
196 
197     if  ( use < hc->hcInHeaderBuffer )
198 	{
199 	memmove( hc->hcHeaderBuffer, hc->hcHeaderBuffer+ use,
200 						hc->hcInHeaderBuffer- use );
201 	}
202 
203     hc->hcInHeaderBuffer -= use;
204     hc->hcUseHeaderBuffer -= use;
205 
206     return use;
207     }
208 
sioInHttpReadHeaderBytes(void * voidhc,unsigned char * buffer,unsigned int count)209 static int sioInHttpReadHeaderBytes(	void *		voidhc,
210 					unsigned char *	buffer,
211 					unsigned int	count )
212     {
213     HttpConnection *	hc= (HttpConnection *)voidhc;
214 
215     if  ( hc->hcCommunicationPhase == CP_ReadHeader )
216 	{
217 	unsigned int	done= 0;
218 
219 	if  ( hc->hcPhases[CP_ReadHeader]
220 				    & (HCphaseEXHAUSTED|HCphaseCLOSED)	)
221 	    {
222 	    LXDEB(hc->hcCommunicationPhase,hc->hcPhases[CP_ReadHeader]);
223 	    return -1;
224 	    }
225 
226 	while( done < count )
227 	    {
228 	    int		got;
229 	    int		crlf2;
230 
231 	    if  ( hc->hcUseHeaderBuffer > 0 )
232 		{
233 		got= sioHttpUseHeaderBuffer( hc, buffer+ done, count- done );
234 
235 		done += got; continue;
236 		}
237 
238 	    if  ( hc->hcPhases[CP_ReadHeader] & HCphaseGOT_END )
239 		{
240 		hc->hcPhases[CP_ReadHeader] |= HCphaseEXHAUSTED;
241 		break;
242 		}
243 
244 	    got= read( hc->hcFd,
245 			    hc->hcHeaderBuffer+ hc->hcInHeaderBuffer,
246 			    SIOsizBUF- hc->hcInHeaderBuffer );
247 	    if  ( got < 0 )
248 		{ LSDEB(hc->hcFd,strerror(errno)); got= 0;	}
249 
250 	    if  ( done == 0 && got <= 0 )
251 		{ hc->hcPhases[CP_ReadHeader] |= HCphaseEXHAUSTED;	}
252 	    if  ( got <= 0 )
253 		{ break;	}
254 
255 	    got= hc->hcInHeaderBuffer+ got;
256 
257 	    for ( crlf2= 0; crlf2 < got- 4; crlf2++ )
258 		{
259 		if  ( ! memcmp( hc->hcHeaderBuffer+ crlf2, "\r\n\r\n", 4 ) )
260 		    { break;	}
261 		}
262 
263 	    if  ( crlf2 < got- 4 )
264 		{
265 		hc->hcUseHeaderBuffer= crlf2+ 4;
266 		hc->hcPhases[CP_ReadHeader] |= HCphaseGOT_END;
267 		}
268 	    else{
269 		hc->hcUseHeaderBuffer= crlf2;
270 		}
271 
272 	    hc->hcInHeaderBuffer= got;
273 	    }
274 
275 	return done;
276 	}
277     else{ LDEB(hc->hcCommunicationPhase); return -1;	}
278     }
279 
sioHttpCloseHeaderInput(void * voidhc)280 static int sioHttpCloseHeaderInput(       void *  voidhc )
281     {
282     int			rval= 0;
283     HttpConnection *	hc= (HttpConnection *)voidhc;
284 
285     if  ( hc->hcCommunicationPhase == CP_ReadHeader )
286 	{
287 	while( ! ( hc->hcPhases[CP_ReadHeader] & HCphaseEXHAUSTED ) )
288 	    {
289 	    int			done;
290 	    unsigned char	b[SIOsizBUF];
291 
292 	    done= sioInHttpReadHeaderBytes( voidhc, b, SIOsizBUF );
293 	    if  ( done < 0 )
294 		{ LDEB(done); return -1;	}
295 	    }
296 
297 	hc->hcPhases[hc->hcCommunicationPhase++] |= HCphaseCLOSED;
298 	while( hc->hcCommunicationPhase < CP__COUNT			&&
299 	       ! (hc->hcPhases[hc->hcCommunicationPhase] & HCphaseUSED)	)
300 	    { hc->hcCommunicationPhase++;	}
301 	}
302 
303     return rval;
304     }
305 
306 /************************************************************************/
307 /*									*/
308 /*  The body stream. (GET,POST,etc)					*/
309 /*									*/
310 /************************************************************************/
311 
sioHttpCloseBodyInput(void * voidhc)312 static int sioHttpCloseBodyInput(       void *  voidhc )
313     {
314     int			rval= 0;
315     HttpConnection *	hc= (HttpConnection *)voidhc;
316 
317     if  ( close( hc->hcFd ) )
318 	{ LSDEB(hc->hcFd,strerror(errno)); rval= -1;	}
319 
320     utilCleanMemoryBuffer( &(hc->hcRequestHeaders) );
321     utilCleanMemoryBuffer( &(hc->hcRequestBody) );
322 
323     free( voidhc );
324 
325     return rval;
326     }
327 
sioInHttpReadBodyBytes(void * voidhc,unsigned char * buffer,unsigned int count)328 static int sioInHttpReadBodyBytes(	void *		voidhc,
329 					unsigned char *	buffer,
330 					unsigned int	count )
331     {
332     HttpConnection *	hc= (HttpConnection *)voidhc;
333 
334     if  ( hc->hcCommunicationPhase == CP_ReadHeader )
335 	{
336 	if  ( sioHttpCloseHeaderInput( voidhc ) )
337 	    { LDEB(hc->hcCommunicationPhase); return -1;	}
338 	}
339 
340     if  ( hc->hcCommunicationPhase == CP_ReadBody )
341 	{
342 	int		done= 0;
343 
344 	if  ( hc->hcPhases[CP_ReadBody]
345 				    & (HCphaseEXHAUSTED|HCphaseCLOSED)	)
346 	    {
347 	    LXDEB(hc->hcCommunicationPhase,hc->hcPhases[CP_ReadBody]);
348 	    return -1;
349 	    }
350 
351 	while( done < count )
352 	    {
353 	    int		got;
354 
355 	    if  ( hc->hcInHeaderBuffer > 0 )
356 		{
357 		hc->hcUseHeaderBuffer= hc->hcInHeaderBuffer;
358 
359 		got= sioHttpUseHeaderBuffer( hc, buffer+ done, count- done );
360 
361 		done += got;
362 		continue;
363 		}
364 
365 	    got= read( hc->hcFd, buffer+ done, count- done );
366 
367 	    if  ( got < 0 )
368 		{ LSDEB(hc->hcFd,strerror(errno)); got= 0;		}
369 	    if  ( done == 0 && got <= 0 )
370 		{ hc->hcPhases[CP_ReadBody] |= HCphaseEXHAUSTED;	}
371 	    if  ( got <= 0 )
372 		{ break;	}
373 
374 	    done += got;
375 	    }
376 
377 	return done;
378 	}
379     else{ LDEB(hc->hcCommunicationPhase); return -1;	}
380     }
381 
382 /************************************************************************/
383 /*									*/
384 /*  Make a HTTP request and return streams to write bytes to the other	*/
385 /*  side, and then read bytes from the other side.			*/
386 /*									*/
387 /************************************************************************/
388 
sioHttpInitConnection(HttpConnection * hc)389 static void sioHttpInitConnection(	HttpConnection *	hc )
390     {
391     hc->hcFd= -1;
392     hc->hcCommunicationPhase= CP_Unknown;
393     memset( hc->hcPhases, HCphaseUNUSED, CP__COUNT );
394 
395     utilInitMemoryBuffer( &(hc->hcRequestHeaders) );
396     utilInitMemoryBuffer( &(hc->hcRequestBody) );
397 
398     hc->hcHeaderBuffer[0]= '\0';
399 
400     hc->hcInHeaderBuffer= 0;
401     hc->hcUseHeaderBuffer= 0;
402 
403     return;
404     }
405 
sioHttpMakeRequest(int fd,const char * url,const char * method)406 static int sioHttpMakeRequest(		int			fd,
407 					const char *		url,
408 					const char *		method )
409     {
410     char *			request= (char *)0;
411     int				requestLength;
412 
413     requestLength= 0;
414     requestLength += strlen( method )+ 1;	/*  "GET "		*/
415     requestLength += strlen( url );		/*  url			*/
416     requestLength += 9;				/*  " HTTP/1.0"		*/
417     requestLength += 2;				/*  "\r\n"		*/
418 
419     request= (char *)malloc( requestLength+ 1 );
420     if  ( ! request )
421 	{ XDEB(request); return -1;	}
422 
423     sprintf( request, "%s %s HTTP/1.0\r\n", method, url );
424 
425     if  ( write( fd, request, requestLength ) != requestLength )
426 	{
427 	LSDEB(requestLength,strerror(errno));
428 	free( request );
429 	return -1;
430 	}
431 
432     free( request );
433     return 0;
434     }
435 
sioHttpOpen(SimpleInputStream ** pSisBody,SimpleInputStream ** pSisHeader,SimpleOutputStream ** pSosBody,SimpleOutputStream ** pSosHeader,const char * host,const char * port,const char * url,const char * method,void * through,APP_COMPLAIN complain)436 int sioHttpOpen(	SimpleInputStream **	pSisBody,
437 			SimpleInputStream **	pSisHeader,
438 			SimpleOutputStream **	pSosBody,
439 			SimpleOutputStream **	pSosHeader,
440 			const char *		host,
441 			const char *		port,
442 			const char *		url,
443 			const char *		method,
444 			void *			through,
445 			APP_COMPLAIN		complain )
446     {
447     int				fd= -1;
448     HttpConnection *		hc= (HttpConnection *)0;
449 
450     SimpleInputStream *		sisBody= (SimpleInputStream *)0;
451     SimpleInputStream *		sisHeader= (SimpleInputStream *)0;
452     SimpleOutputStream *	sosBody= (SimpleOutputStream *)0;
453     SimpleOutputStream *	sosHeader= (SimpleOutputStream *)0;
454 
455     int				openBodyIn= 0;
456     int				openBodyOut= 0;
457 
458     if  ( ! port )
459 	{ port= "http";	}
460 
461     if  ( strcmp( method, "HEAD" ) )
462 	{
463 	if  ( ! pSisBody )
464 	    { SXDEB(method,pSisBody); goto failure;	}
465 
466 	openBodyIn= 1;
467 	}
468 
469     if  ( ! strcmp( method, "PUT" )	||
470 	  ! strcmp( method, "POST" )	)
471 	{
472 	if  ( ! pSosBody )
473 	    { SXDEB(method,pSosBody); goto failure;	}
474 
475 	openBodyOut= 1;
476 	}
477 
478     fd= appOpenSocket( host, port, through, complain );
479     if  ( fd < 0 )
480 	{ LDEB(fd); goto failure;	}
481 
482     if  ( sioHttpMakeRequest( fd, url, method ) )
483 	{ LDEB(fd); goto failure;	}
484 
485     hc= (HttpConnection *)malloc( sizeof(HttpConnection) );
486     if  ( ! hc )
487 	{ XDEB(hc); goto failure;	}
488     sioHttpInitConnection( hc );
489 
490     hc->hcFd= fd;
491 
492     /******/
493 
494     if  ( pSisHeader )
495 	{
496 	sisHeader= sioInOpen( (void *)hc, sioInHttpReadHeaderBytes,
497 						sioHttpCloseHeaderInput );
498 	if  ( ! sisHeader )
499 	    { XDEB(sisHeader); goto failure;	}
500 
501 	hc->hcPhases[CP_ReadHeader] |= HCphaseUSED;
502 	}
503 
504     /******/
505 
506     if  ( openBodyIn )
507 	{
508 	sisBody= sioInOpen( (void *)hc, sioInHttpReadBodyBytes,
509 						    sioHttpCloseBodyInput );
510 	if  ( ! sisBody )
511 	    { XDEB(sisBody); goto failure;	}
512 
513 	hc->hcPhases[CP_ReadBody] |= HCphaseUSED;
514 	}
515 
516     /******/
517 
518     if  ( pSosHeader )
519 	{
520 	sosHeader= sioOutOpen( (void *)hc, sioOutHttpWriteHeaderBytes,
521 						    sioHttpCloseHeaderOutput );
522 	if  ( ! sosHeader )
523 	    { XDEB(sosHeader); goto failure;	}
524 
525 	hc->hcPhases[CP_WriteHeader] |= HCphaseUSED;
526 	}
527 
528     /******/
529 
530     if  ( openBodyOut )
531 	{
532 	sosBody= sioOutOpen( (void *)hc, sioOutHttpWriteBodyBytes,
533 						    sioHttpCloseBodyOutput );
534 	if  ( ! sosBody )
535 	    { XDEB(sosBody); goto failure;	}
536 
537 	hc->hcPhases[CP_WriteBody] |= HCphaseUSED;
538 	}
539     else{ hc->hcPhases[CP_ReadHeader] |= HCphaseUSED;	}
540 
541     /******/
542 
543     hc->hcPhases[hc->hcCommunicationPhase++] |= HCphaseCLOSED;
544     while( hc->hcCommunicationPhase < CP__COUNT				&&
545 	   ! (hc->hcPhases[hc->hcCommunicationPhase] & HCphaseUSED)	)
546 	{ hc->hcCommunicationPhase++;	}
547 
548     /******/
549 
550     if  ( pSisBody )
551 	{ *pSisBody= sisBody;		}
552     if  ( pSisHeader )
553 	{ *pSisHeader= sisHeader;	}
554     if  ( pSosBody )
555 	{ *pSosBody= sosBody;		}
556     if  ( pSosHeader )
557 	{ *pSosHeader= sosHeader;	}
558 
559     return 0;
560 
561   failure:
562     LDEB(1);
563 
564     if  ( hc )
565 	{
566 	utilCleanMemoryBuffer( &(hc->hcRequestHeaders) );
567 	utilCleanMemoryBuffer( &(hc->hcRequestBody) );
568 
569 	free( hc );
570 	}
571 
572     if  ( fd >= 0 && close( fd ) )
573 	{ LSDEB(fd,strerror(errno));	}
574 
575     return -1;
576     }
577