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