root/src/modules/libstomp.c

Revision a5e761d52d97a6681d7c9db3a7707d8af31852f5, 12.4 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

dead assignments, refs #283

  • Property mode set to 100644
Line 
1 /**
2  *
3  * Copyright 2005 LogicBlaze Inc.
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include "apr.h"
21 #include "apr_strings.h"
22 #include "libstomp.h"
23
24 /********************************************************************************
25  *
26  * Used to establish a connection
27  *
28  ********************************************************************************/
29 APR_DECLARE(apr_status_t) stomp_connect(stomp_connection **connection_ref, const char *hostname, int port, apr_pool_t *pool)
30 {
31         apr_status_t rc;
32         int socket_family;
33         stomp_connection *connection=NULL;
34    
35         //
36         // Allocate the connection and a memory pool for the connection.
37         //
38         connection = apr_pcalloc(pool, sizeof(*connection));
39         if( connection == NULL )
40                 return APR_ENOMEM;
41    
42 #define CHECK_SUCCESS if( rc!=APR_SUCCESS ) { return rc; }
43    
44         // Look up the remote address
45         rc = apr_sockaddr_info_get(&connection->remote_sa, hostname, APR_UNSPEC, port, 0, pool);
46         CHECK_SUCCESS;
47        
48         // Create and Connect the socket.
49         socket_family = connection->remote_sa->sa.sin.sin_family;
50         rc = apr_socket_create(&connection->socket, socket_family, SOCK_STREAM, APR_PROTO_TCP, pool);
51         CHECK_SUCCESS; 
52 #undef CHECK_SUCCESS
53
54 #define CHECK_SUCCESS if( rc!=APR_SUCCESS ) { apr_socket_close(connection->socket); return rc; }
55    rc = apr_socket_connect(connection->socket, connection->remote_sa);
56         CHECK_SUCCESS;
57    
58    // Get the Socket Info
59    rc = apr_socket_addr_get(&connection->remote_sa, APR_REMOTE, connection->socket);
60         CHECK_SUCCESS;
61    rc = apr_sockaddr_ip_get(&connection->remote_ip, connection->remote_sa);
62         CHECK_SUCCESS;
63    rc = apr_socket_addr_get(&connection->local_sa, APR_LOCAL, connection->socket);
64         CHECK_SUCCESS;
65    rc = apr_sockaddr_ip_get(&connection->local_ip, connection->local_sa);
66         CHECK_SUCCESS; 
67    
68    // Set socket options.
69    //   rc = apr_socket_timeout_set( connection->socket, 2*APR_USEC_PER_SEC);
70    //   CHECK_SUCCESS;
71    
72 #undef CHECK_SUCCESS
73    
74         *connection_ref = connection;
75         return rc;     
76 }
77
78 APR_DECLARE(apr_status_t) stomp_disconnect(stomp_connection **connection_ref)
79 {
80    apr_status_t result, rc;
81         stomp_connection *connection = *connection_ref;
82    
83    if( connection_ref == NULL || *connection_ref==NULL )
84       return APR_EGENERAL;
85    
86         result = APR_SUCCESS;   
87    rc = apr_socket_shutdown(connection->socket, APR_SHUTDOWN_WRITE);   
88         if( result!=APR_SUCCESS )
89                 result = rc;
90    
91    if( connection->socket != NULL ) {
92       rc = apr_socket_close(connection->socket);
93       if( result!=APR_SUCCESS )
94          result = rc;
95       connection->socket=NULL;
96    }   
97         *connection_ref=NULL;
98         return rc;     
99 }
100
101 /********************************************************************************
102  *
103  * Wrappers around the apr_socket_send and apr_socket_recv calls so that they
104  * read/write their buffers fully.
105  *
106  ********************************************************************************/
107 APR_DECLARE(apr_status_t) stomp_write_buffer(stomp_connection *connection, const char *data, apr_size_t size)
108 {
109    apr_size_t remaining = size;
110         while( remaining>0 ) {
111                 apr_size_t length = remaining;
112                 apr_status_t rc = apr_socket_send(connection->socket, data, &length);
113       data+=length;
114       remaining -= length;
115       if( rc != APR_SUCCESS ) {
116          return rc;
117       }
118         }
119         return APR_SUCCESS;
120 }
121
122 typedef struct data_block_list {
123    char data[1024];
124    struct data_block_list *next;
125 } data_block_list;
126
127 APR_DECLARE(apr_status_t) stomp_read_line(stomp_connection *connection, char **data, int* length, apr_pool_t *pool)
128 {
129    apr_pool_t *tpool;
130    apr_status_t rc;
131    data_block_list *head, *tail;
132    apr_size_t i=0;
133    apr_size_t bytesRead=0;
134    char *p;
135    
136    rc = apr_pool_create(&tpool, pool);
137    if( rc != APR_SUCCESS ) {
138       return rc;
139    }
140      
141    head = tail = apr_pcalloc(tpool, sizeof(data_block_list));
142    if( head == NULL )
143       return APR_ENOMEM;
144
145 #define CHECK_SUCCESS if( rc!=APR_SUCCESS ) { apr_pool_destroy(tpool);  return rc; }
146        
147    while( 1 ) {
148      
149           apr_size_t length = 1;
150       apr_status_t rc = apr_socket_recv(connection->socket, tail->data+i, &length);
151       CHECK_SUCCESS;
152      
153       if( length==1 ) {
154          i++;
155          bytesRead++;
156          
157          // Keep reading bytes till end of line
158          if( tail->data[i-1]=='\n') {
159             // Null terminate the string instead of having the newline
160                     tail->data[i-1] = 0;
161                         break;
162          } else if( tail->data[i-1]==0 ) {
163                         // Encountered 0 before end of line
164                         apr_pool_destroy(tpool);
165                         return APR_EGENERAL;
166                  }
167          
168          // Do we need to allocate a new block?
169          if( i >= sizeof( tail->data) ) {           
170             tail->next = apr_pcalloc(tpool, sizeof(data_block_list));
171             if( tail->next == NULL ) {
172                apr_pool_destroy(tpool);
173                return APR_ENOMEM;
174             }
175             tail=tail->next;
176             i=0;
177          }
178       }     
179         }
180
181 #undef CHECK_SUCCESS
182    // Now we have the whole frame and know how big it is.  Allocate it's buffer
183    *data = apr_pcalloc(pool, bytesRead);
184    p = *data;
185    if( p==NULL ) {
186       apr_pool_destroy(tpool);
187       return APR_ENOMEM;
188    }
189
190    // Copy the frame over to the new buffer.
191    *length = bytesRead - 1;
192    for( ;head != NULL; head = head->next ) {
193       int len = bytesRead > sizeof(head->data) ? sizeof(head->data) : bytesRead;
194       memcpy(p,head->data,len);
195       p+=len;
196       bytesRead-=len;
197    }
198    
199    apr_pool_destroy(tpool);
200    return APR_SUCCESS;
201 }
202
203 APR_DECLARE(apr_status_t) stomp_read_buffer(stomp_connection *connection, char **data, apr_pool_t *pool)
204 {
205    apr_pool_t *tpool;
206    apr_status_t rc;
207    data_block_list *head, *tail;
208    apr_size_t i=0;
209    apr_size_t bytesRead=0;
210    char *p;
211    
212    rc = apr_pool_create(&tpool, pool);
213    if( rc != APR_SUCCESS ) {
214       return rc;
215    }
216      
217    head = tail = apr_pcalloc(tpool, sizeof(data_block_list));
218    if( head == NULL )
219       return APR_ENOMEM;
220    
221 #define CHECK_SUCCESS if( rc!=APR_SUCCESS ) { apr_pool_destroy(tpool);  return rc; }
222    
223    // Keep reading bytes till end of frame is encountered.
224         while( 1 ) {
225      
226                 apr_size_t length = 1;
227       apr_status_t rc = apr_socket_recv(connection->socket, tail->data+i, &length);
228       CHECK_SUCCESS;
229      
230       if( length==1 ) {
231          i++;
232          bytesRead++;
233          
234          // Keep reading bytes till end of frame
235          if( tail->data[i-1]==0 ) {
236             char endline[1];
237             // We expect a newline after the null.
238             apr_socket_recv(connection->socket, endline, &length);
239             CHECK_SUCCESS;
240             if( endline[0] != '\n' ) {
241                return APR_EGENERAL;
242             }
243             break;
244          }
245          
246          // Do we need to allocate a new block?
247          if( i >= sizeof( tail->data) ) {           
248             tail->next = apr_pcalloc(tpool, sizeof(data_block_list));
249             if( tail->next == NULL ) {
250                apr_pool_destroy(tpool);
251                return APR_ENOMEM;
252             }
253             tail=tail->next;
254             i=0;
255          }
256       }     
257         }
258 #undef CHECK_SUCCESS
259    
260    // Now we have the whole frame and know how big it is.  Allocate it's buffer
261    *data = apr_pcalloc(pool, bytesRead);
262    p = *data;
263    if( p==NULL ) {
264       apr_pool_destroy(tpool);
265       return APR_ENOMEM;
266    }
267    
268    // Copy the frame over to the new buffer.
269    for( ;head != NULL; head = head->next ) {
270       int len = bytesRead > sizeof(head->data) ? sizeof(head->data) : bytesRead;
271       memcpy(p,head->data,len);
272       p+=len;
273       bytesRead-=len;
274    }
275    
276    apr_pool_destroy(tpool);
277         return APR_SUCCESS;
278 }
279
280 /********************************************************************************
281  *
282  * Handles reading and writing stomp_frames to and from the connection
283  *
284  ********************************************************************************/
285
286 APR_DECLARE(apr_status_t) stomp_write(stomp_connection *connection, stomp_frame *frame, apr_pool_t* pool) {
287    apr_status_t rc;
288    
289 #define CHECK_SUCCESS if( rc!=APR_SUCCESS ) { return rc; }
290    // Write the command.
291    rc = stomp_write_buffer(connection, frame->command, strlen(frame->command));
292    CHECK_SUCCESS;               
293    rc = stomp_write_buffer(connection, "\n", 1);
294    CHECK_SUCCESS;
295    
296    // Write the headers
297    if( frame->headers != NULL ) {
298      
299       apr_hash_index_t *i;
300       const void *key;
301       void *value;
302       for (i = apr_hash_first(NULL, frame->headers); i; i = apr_hash_next(i)) {
303          apr_hash_this(i, &key, NULL, &value);
304          
305          rc = stomp_write_buffer(connection, key, strlen(key));
306          CHECK_SUCCESS;
307          rc = stomp_write_buffer(connection, ":", 1);
308          CHECK_SUCCESS;
309          rc = stomp_write_buffer(connection, value, strlen(value));
310          CHECK_SUCCESS;
311          rc = stomp_write_buffer(connection, "\n", 1);
312          CHECK_SUCCESS; 
313       }
314
315           if(frame->body_length >= 0) {
316                   apr_pool_t *length_pool;
317                   char *length_string;
318
319                   apr_pool_create(&length_pool, pool);
320                   rc = stomp_write_buffer(connection, "content-length:", 15);
321                   CHECK_SUCCESS;
322                  
323                   length_string = apr_itoa(length_pool, frame->body_length);
324                   rc = stomp_write_buffer(connection, length_string, strlen(length_string));
325                   CHECK_SUCCESS;
326                   rc = stomp_write_buffer(connection, "\n", 1);
327                   CHECK_SUCCESS;
328
329                   apr_pool_destroy(length_pool);
330           }
331    }
332    rc = stomp_write_buffer(connection, "\n", 1);
333    CHECK_SUCCESS;
334    
335    // Write the body.
336    if( frame->body != NULL ) {
337       int body_length = frame->body_length;
338           if(body_length < 0)
339                   body_length = strlen(frame->body);
340       rc = stomp_write_buffer(connection, frame->body, body_length);
341       CHECK_SUCCESS;
342    }
343    rc = stomp_write_buffer(connection, "\0\n", 2);
344    CHECK_SUCCESS;
345      
346 #undef CHECK_SUCCESS
347                    
348    return APR_SUCCESS;
349 }
350
351 APR_DECLARE(apr_status_t) stomp_read(stomp_connection *connection, stomp_frame **frame, apr_pool_t *pool) {
352    
353    apr_status_t rc;
354    stomp_frame *f;
355      
356    f = apr_pcalloc(pool, sizeof(stomp_frame));
357    if( f == NULL )
358       return APR_ENOMEM;
359    
360    f->headers = apr_hash_make(pool);
361    if( f->headers == NULL )
362       return APR_ENOMEM;
363          
364 #define CHECK_SUCCESS if( rc!=APR_SUCCESS ) { return rc; }
365    
366    // Parse the frame out.
367    {
368       char *p;
369           int length;
370      
371       // Parse the command.
372           rc = stomp_read_line(connection, &p, &length, pool);
373           CHECK_SUCCESS;
374
375       f->command = p;
376      
377       // Start parsing the headers.
378       while( 1 ) {
379          rc = stomp_read_line(connection, &p, &length, pool);
380                  CHECK_SUCCESS;
381                  
382                  // Done with headers
383                  if(length == 0)
384                         break;
385
386          {
387             // Parse the header line.
388             char *p2;
389             void *key;
390             void *value;
391            
392             p2 = strstr(p,":");
393             if( p2 == NULL ) {
394                // Expected at 1 : to delimit the key from the value.
395                return APR_EGENERAL;
396             }
397            
398             // Null terminate the key
399             *p2=0;           
400             key = p;
401            
402             // The rest if the value.
403             value = p2+1;
404            
405             // Insert key/value into hash table.
406             apr_hash_set(f->headers, key, APR_HASH_KEY_STRING, value);           
407          }
408       }
409      
410       // Check for content length
411           {
412                   char* content_length = apr_hash_get(f->headers, "content-length", APR_HASH_KEY_STRING);
413                   if(content_length) {
414                           char endbuffer[2];
415                           apr_size_t length = 2;
416
417                           f->body_length = atoi(content_length);
418                           f->body = apr_pcalloc(pool, f->body_length);
419                           rc = apr_socket_recv(connection->socket, f->body, &f->body_length);
420                           CHECK_SUCCESS;
421
422                           // Expect a \n after the end
423                           rc = apr_socket_recv(connection->socket, endbuffer, &length);
424                           CHECK_SUCCESS;
425                           if(length != 2 || endbuffer[0] != '\0' || endbuffer[1] != '\n')
426                                   return APR_EGENERAL;
427                   }
428                   else
429                   {
430                           // The remainder of the buffer (including the \n at the end) is the body)
431                           rc = stomp_read_buffer(connection, &f->body, pool);
432                           CHECK_SUCCESS;
433                   }
434           }
435    }
436    
437 #undef CHECK_SUCCESS
438    *frame = f;
439         return APR_SUCCESS;
440 }
Note: See TracBrowser for help on using the browser.