1 /* ==================================================================== 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 * ==================================================================== 19 */ 20 21 #ifndef SERF_BUCKET_TYPES_H 22 #define SERF_BUCKET_TYPES_H 23 24 #include <apr_mmap.h> 25 #include <apr_hash.h> 26 27 /* this header and serf.h refer to each other, so take a little extra care */ 28 #ifndef SERF_H 29 #include "serf.h" 30 #endif 31 32 33 /** 34 * @file serf_bucket_types.h 35 * @brief serf-supported bucket types 36 */ 37 /* ### this whole file needs docco ... */ 38 39 #ifdef __cplusplus 40 extern "C" { 41 #endif 42 43 /* ==================================================================== */ 44 45 46 extern const serf_bucket_type_t serf_bucket_type_request; 47 #define SERF_BUCKET_IS_REQUEST(b) SERF_BUCKET_CHECK((b), request) 48 49 serf_bucket_t *serf_bucket_request_create( 50 const char *method, 51 const char *URI, 52 serf_bucket_t *body, 53 serf_bucket_alloc_t *allocator); 54 55 /* Send a Content-Length header with @a len. The @a body bucket should 56 contain precisely that much data. */ 57 void serf_bucket_request_set_CL( 58 serf_bucket_t *bucket, 59 apr_int64_t len); 60 61 serf_bucket_t *serf_bucket_request_get_headers( 62 serf_bucket_t *request); 63 64 void serf_bucket_request_become( 65 serf_bucket_t *bucket, 66 const char *method, 67 const char *uri, 68 serf_bucket_t *body); 69 70 /** 71 * Sets the root url of the remote host. If this request contains a relative 72 * url, it will be prefixed with the root url to form an absolute url. 73 * @a bucket is the request bucket. @a root_url is the absolute url of the 74 * root of the remote host, without the closing '/'. 75 */ 76 void serf_bucket_request_set_root( 77 serf_bucket_t *bucket, 78 const char *root_url); 79 80 /* ==================================================================== */ 81 82 83 extern const serf_bucket_type_t serf_bucket_type_response; 84 #define SERF_BUCKET_IS_RESPONSE(b) SERF_BUCKET_CHECK((b), response) 85 86 serf_bucket_t *serf_bucket_response_create( 87 serf_bucket_t *stream, 88 serf_bucket_alloc_t *allocator); 89 90 #define SERF_HTTP_VERSION(major, minor) ((major) * 1000 + (minor)) 91 #define SERF_HTTP_11 SERF_HTTP_VERSION(1, 1) 92 #define SERF_HTTP_10 SERF_HTTP_VERSION(1, 0) 93 #define SERF_HTTP_VERSION_MAJOR(shv) ((int)shv / 1000) 94 #define SERF_HTTP_VERSION_MINOR(shv) ((int)shv % 1000) 95 96 typedef struct { 97 int version; 98 int code; 99 const char *reason; 100 } serf_status_line; 101 102 /** 103 * Return the Status-Line information, if available. This function 104 * works like other bucket read functions: it may return APR_EAGAIN or 105 * APR_EOF to signal the state of the bucket for reading. A return 106 * value of APR_SUCCESS will always indicate that status line 107 * information was returned; for other return values the caller must 108 * check the version field in @a sline. A value of 0 means that the 109 * data is not (yet) present. 110 */ 111 apr_status_t serf_bucket_response_status( 112 serf_bucket_t *bkt, 113 serf_status_line *sline); 114 115 /** 116 * Wait for the HTTP headers to be processed for a @a response. 117 * 118 * If the headers are available, APR_SUCCESS is returned. 119 * If the headers aren't available, APR_EAGAIN is returned. 120 */ 121 apr_status_t serf_bucket_response_wait_for_headers( 122 serf_bucket_t *response); 123 124 /** 125 * Get the headers bucket for @a response. 126 */ 127 serf_bucket_t *serf_bucket_response_get_headers( 128 serf_bucket_t *response); 129 130 /** 131 * Advise the response @a bucket that this was from a HEAD request and 132 * that it should not expect to see a response body. 133 */ 134 void serf_bucket_response_set_head( 135 serf_bucket_t *bucket); 136 137 /* ==================================================================== */ 138 139 extern const serf_bucket_type_t serf_bucket_type_response_body; 140 #define SERF_BUCKET_IS_RESPONSE_BODY(b) SERF_BUCKET_CHECK((b), response_body) 141 142 serf_bucket_t *serf_bucket_response_body_create( 143 serf_bucket_t *stream, 144 apr_uint64_t limit, 145 serf_bucket_alloc_t *allocator); 146 147 /* ==================================================================== */ 148 149 extern const serf_bucket_type_t serf_bucket_type_bwtp_frame; 150 #define SERF_BUCKET_IS_BWTP_FRAME(b) SERF_BUCKET_CHECK((b), bwtp_frame) 151 152 extern const serf_bucket_type_t serf_bucket_type_bwtp_incoming_frame; 153 #define SERF_BUCKET_IS_BWTP_INCOMING_FRAME(b) SERF_BUCKET_CHECK((b), bwtp_incoming_frame) 154 155 int serf_bucket_bwtp_frame_get_channel( 156 serf_bucket_t *hdr); 157 158 int serf_bucket_bwtp_frame_get_type( 159 serf_bucket_t *hdr); 160 161 const char *serf_bucket_bwtp_frame_get_phrase( 162 serf_bucket_t *hdr); 163 164 serf_bucket_t *serf_bucket_bwtp_frame_get_headers( 165 serf_bucket_t *hdr); 166 167 serf_bucket_t *serf_bucket_bwtp_channel_open( 168 int channel, 169 const char *URI, 170 serf_bucket_alloc_t *allocator); 171 172 serf_bucket_t *serf_bucket_bwtp_channel_close( 173 int channel, 174 serf_bucket_alloc_t *allocator); 175 176 serf_bucket_t *serf_bucket_bwtp_header_create( 177 int channel, 178 const char *phrase, 179 serf_bucket_alloc_t *allocator); 180 181 serf_bucket_t *serf_bucket_bwtp_message_create( 182 int channel, 183 serf_bucket_t *body, 184 serf_bucket_alloc_t *allocator); 185 186 serf_bucket_t *serf_bucket_bwtp_incoming_frame_create( 187 serf_bucket_t *bkt, 188 serf_bucket_alloc_t *allocator); 189 190 apr_status_t serf_bucket_bwtp_incoming_frame_wait_for_headers( 191 serf_bucket_t *bkt); 192 193 /* ==================================================================== */ 194 195 196 extern const serf_bucket_type_t serf_bucket_type_aggregate; 197 #define SERF_BUCKET_IS_AGGREGATE(b) SERF_BUCKET_CHECK((b), aggregate) 198 199 typedef apr_status_t (*serf_bucket_aggregate_eof_t)( 200 void *baton, 201 serf_bucket_t *aggregate_bucket); 202 203 /** serf_bucket_aggregate_cleanup will instantly destroy all buckets in 204 the aggregate bucket that have been read completely. Whereas normally, 205 these buckets are destroyed on every read operation. */ 206 void serf_bucket_aggregate_cleanup( 207 serf_bucket_t *bucket, 208 serf_bucket_alloc_t *allocator); 209 210 serf_bucket_t *serf_bucket_aggregate_create( 211 serf_bucket_alloc_t *allocator); 212 213 /* Creates a stream bucket. 214 A stream bucket is like an aggregate bucket, but: 215 - it doesn't destroy its child buckets on cleanup 216 - one can always keep adding child buckets, the handler FN should return 217 APR_EOF when no more buckets will be added. 218 219 Note: keep this factory function internal for now. If it turns out this 220 bucket type is useful outside serf, we should make it an actual separate 221 type. 222 */ 223 serf_bucket_t *serf__bucket_stream_create( 224 serf_bucket_alloc_t *allocator, 225 serf_bucket_aggregate_eof_t fn, 226 void *baton); 227 228 /** Transform @a bucket in-place into an aggregate bucket. */ 229 void serf_bucket_aggregate_become( 230 serf_bucket_t *bucket); 231 232 void serf_bucket_aggregate_prepend( 233 serf_bucket_t *aggregate_bucket, 234 serf_bucket_t *prepend_bucket); 235 236 void serf_bucket_aggregate_append( 237 serf_bucket_t *aggregate_bucket, 238 serf_bucket_t *append_bucket); 239 240 void serf_bucket_aggregate_hold_open( 241 serf_bucket_t *aggregate_bucket, 242 serf_bucket_aggregate_eof_t fn, 243 void *baton); 244 245 void serf_bucket_aggregate_prepend_iovec( 246 serf_bucket_t *aggregate_bucket, 247 struct iovec *vecs, 248 int vecs_count); 249 250 void serf_bucket_aggregate_append_iovec( 251 serf_bucket_t *aggregate_bucket, 252 struct iovec *vecs, 253 int vecs_count); 254 255 /* ==================================================================== */ 256 257 258 extern const serf_bucket_type_t serf_bucket_type_file; 259 #define SERF_BUCKET_IS_FILE(b) SERF_BUCKET_CHECK((b), file) 260 261 serf_bucket_t *serf_bucket_file_create( 262 apr_file_t *file, 263 serf_bucket_alloc_t *allocator); 264 265 266 /* ==================================================================== */ 267 268 269 extern const serf_bucket_type_t serf_bucket_type_socket; 270 #define SERF_BUCKET_IS_SOCKET(b) SERF_BUCKET_CHECK((b), socket) 271 272 serf_bucket_t *serf_bucket_socket_create( 273 apr_socket_t *skt, 274 serf_bucket_alloc_t *allocator); 275 276 /** 277 * Call @a progress_func every time bytes are read from the socket, pass 278 * the number of bytes read. 279 * 280 * When using serf's bytes read & written progress indicator, pass 281 * @a serf_context_progress_delta for progress_func and the serf_context for 282 * progress_baton. 283 */ 284 void serf_bucket_socket_set_read_progress_cb( 285 serf_bucket_t *bucket, 286 const serf_progress_t progress_func, 287 void *progress_baton); 288 289 /* ==================================================================== */ 290 291 292 extern const serf_bucket_type_t serf_bucket_type_simple; 293 #define SERF_BUCKET_IS_SIMPLE(b) SERF_BUCKET_CHECK((b), simple) 294 295 typedef void (*serf_simple_freefunc_t)( 296 void *baton, 297 const char *data); 298 299 serf_bucket_t *serf_bucket_simple_create( 300 const char *data, 301 apr_size_t len, 302 serf_simple_freefunc_t freefunc, 303 void *freefunc_baton, 304 serf_bucket_alloc_t *allocator); 305 306 /** 307 * Equivalent to serf_bucket_simple_create, except that the bucket takes 308 * ownership of a private copy of the data. 309 */ 310 serf_bucket_t *serf_bucket_simple_copy_create( 311 const char *data, 312 apr_size_t len, 313 serf_bucket_alloc_t *allocator); 314 315 /** 316 * Equivalent to serf_bucket_simple_create, except that the bucket assumes 317 * responsibility for freeing the data on this allocator without making 318 * a copy. It is assumed that data was created by a call from allocator. 319 */ 320 serf_bucket_t *serf_bucket_simple_own_create( 321 const char *data, 322 apr_size_t len, 323 serf_bucket_alloc_t *allocator); 324 325 #define SERF_BUCKET_SIMPLE_STRING(s,a) \ 326 serf_bucket_simple_create(s, strlen(s), NULL, NULL, a); 327 328 #define SERF_BUCKET_SIMPLE_STRING_LEN(s,l,a) \ 329 serf_bucket_simple_create(s, l, NULL, NULL, a); 330 331 /* ==================================================================== */ 332 333 334 /* Note: apr_mmap_t is always defined, but if APR doesn't have mmaps, then 335 the caller can never create an apr_mmap_t to pass to this function. */ 336 337 extern const serf_bucket_type_t serf_bucket_type_mmap; 338 #define SERF_BUCKET_IS_MMAP(b) SERF_BUCKET_CHECK((b), mmap) 339 340 serf_bucket_t *serf_bucket_mmap_create( 341 apr_mmap_t *mmap, 342 serf_bucket_alloc_t *allocator); 343 344 345 /* ==================================================================== */ 346 347 348 extern const serf_bucket_type_t serf_bucket_type_headers; 349 #define SERF_BUCKET_IS_HEADERS(b) SERF_BUCKET_CHECK((b), headers) 350 351 serf_bucket_t *serf_bucket_headers_create( 352 serf_bucket_alloc_t *allocator); 353 354 /** 355 * Set, default: value copied. 356 * 357 * Set the specified @a header within the bucket, copying the @a value 358 * into space from this bucket's allocator. The header is NOT copied, 359 * so it should remain in scope at least as long as the bucket. 360 */ 361 void serf_bucket_headers_set( 362 serf_bucket_t *headers_bucket, 363 const char *header, 364 const char *value); 365 366 /** 367 * Set, copies: header and value copied. 368 * 369 * Copy the specified @a header and @a value into the bucket, using space 370 * from this bucket's allocator. 371 */ 372 void serf_bucket_headers_setc( 373 serf_bucket_t *headers_bucket, 374 const char *header, 375 const char *value); 376 377 /** 378 * Set, no copies. 379 * 380 * Set the specified @a header and @a value into the bucket, without 381 * copying either attribute. Both attributes should remain in scope at 382 * least as long as the bucket. 383 * 384 * @note In the case where a header already exists this will result 385 * in a reallocation and copy, @see serf_bucket_headers_setn. 386 */ 387 void serf_bucket_headers_setn( 388 serf_bucket_t *headers_bucket, 389 const char *header, 390 const char *value); 391 392 /** 393 * Set, extended: fine grained copy control of header and value. 394 * 395 * Set the specified @a header, with length @a header_size with the 396 * @a value, and length @a value_size, into the bucket. The header will 397 * be copied if @a header_copy is set, and the value is copied if 398 * @a value_copy is set. If the values are not copied, then they should 399 * remain in scope at least as long as the bucket. 400 * 401 * If @a headers_bucket already contains a header with the same name 402 * as @a header, then append @a value to the existing value, 403 * separating with a comma (as per RFC 2616, section 4.2). In this 404 * case, the new value must be allocated and the header re-used, so 405 * behave as if @a value_copy were true and @a header_copy false. 406 */ 407 void serf_bucket_headers_setx( 408 serf_bucket_t *headers_bucket, 409 const char *header, 410 apr_size_t header_size, 411 int header_copy, 412 const char *value, 413 apr_size_t value_size, 414 int value_copy); 415 416 const char *serf_bucket_headers_get( 417 serf_bucket_t *headers_bucket, 418 const char *header); 419 420 /** 421 * @param baton opaque baton as passed to @see serf_bucket_headers_do 422 * @param key The header key from this iteration through the table 423 * @param value The header value from this iteration through the table 424 */ 425 typedef int (serf_bucket_headers_do_callback_fn_t)( 426 void *baton, 427 const char *key, 428 const char *value); 429 430 /** 431 * Iterates over all headers of the message and invokes the callback 432 * function with header key and value. Stop iterating when no more 433 * headers are available or when the callback function returned a 434 * non-0 value. 435 * 436 * @param headers_bucket headers to iterate over 437 * @param func callback routine to invoke for every header in the bucket 438 * @param baton baton to pass on each invocation to func 439 */ 440 void serf_bucket_headers_do( 441 serf_bucket_t *headers_bucket, 442 serf_bucket_headers_do_callback_fn_t func, 443 void *baton); 444 445 446 /* ==================================================================== */ 447 448 449 extern const serf_bucket_type_t serf_bucket_type_chunk; 450 #define SERF_BUCKET_IS_CHUNK(b) SERF_BUCKET_CHECK((b), chunk) 451 452 serf_bucket_t *serf_bucket_chunk_create( 453 serf_bucket_t *stream, 454 serf_bucket_alloc_t *allocator); 455 456 457 /* ==================================================================== */ 458 459 460 extern const serf_bucket_type_t serf_bucket_type_dechunk; 461 #define SERF_BUCKET_IS_DECHUNK(b) SERF_BUCKET_CHECK((b), dechunk) 462 463 serf_bucket_t *serf_bucket_dechunk_create( 464 serf_bucket_t *stream, 465 serf_bucket_alloc_t *allocator); 466 467 468 /* ==================================================================== */ 469 470 471 extern const serf_bucket_type_t serf_bucket_type_deflate; 472 #define SERF_BUCKET_IS_DEFLATE(b) SERF_BUCKET_CHECK((b), deflate) 473 474 #define SERF_DEFLATE_GZIP 0 475 #define SERF_DEFLATE_DEFLATE 1 476 477 serf_bucket_t *serf_bucket_deflate_create( 478 serf_bucket_t *stream, 479 serf_bucket_alloc_t *allocator, 480 int format); 481 482 483 /* ==================================================================== */ 484 485 486 extern const serf_bucket_type_t serf_bucket_type_limit; 487 #define SERF_BUCKET_IS_LIMIT(b) SERF_BUCKET_CHECK((b), limit) 488 489 serf_bucket_t *serf_bucket_limit_create( 490 serf_bucket_t *stream, 491 apr_uint64_t limit, 492 serf_bucket_alloc_t *allocator); 493 494 495 /* ==================================================================== */ 496 #define SERF_SSL_CERT_NOTYETVALID 1 497 #define SERF_SSL_CERT_EXPIRED 2 498 #define SERF_SSL_CERT_UNKNOWNCA 4 499 #define SERF_SSL_CERT_SELF_SIGNED 8 500 #define SERF_SSL_CERT_UNKNOWN_FAILURE 16 501 #define SERF_SSL_CERT_REVOKED 32 502 503 extern const serf_bucket_type_t serf_bucket_type_ssl_encrypt; 504 #define SERF_BUCKET_IS_SSL_ENCRYPT(b) SERF_BUCKET_CHECK((b), ssl_encrypt) 505 506 typedef struct serf_ssl_context_t serf_ssl_context_t; 507 typedef struct serf_ssl_certificate_t serf_ssl_certificate_t; 508 509 typedef apr_status_t (*serf_ssl_need_client_cert_t)( 510 void *data, 511 const char **cert_path); 512 513 typedef apr_status_t (*serf_ssl_need_cert_password_t)( 514 void *data, 515 const char *cert_path, 516 const char **password); 517 518 typedef apr_status_t (*serf_ssl_need_server_cert_t)( 519 void *data, 520 int failures, 521 const serf_ssl_certificate_t *cert); 522 523 typedef apr_status_t (*serf_ssl_server_cert_chain_cb_t)( 524 void *data, 525 int failures, 526 int error_depth, 527 const serf_ssl_certificate_t * const * certs, 528 apr_size_t certs_len); 529 530 void serf_ssl_client_cert_provider_set( 531 serf_ssl_context_t *context, 532 serf_ssl_need_client_cert_t callback, 533 void *data, 534 void *cache_pool); 535 536 void serf_ssl_client_cert_password_set( 537 serf_ssl_context_t *context, 538 serf_ssl_need_cert_password_t callback, 539 void *data, 540 void *cache_pool); 541 542 /** 543 * Set a callback to override the default SSL server certificate validation 544 * algorithm. 545 */ 546 void serf_ssl_server_cert_callback_set( 547 serf_ssl_context_t *context, 548 serf_ssl_need_server_cert_t callback, 549 void *data); 550 551 /** 552 * Set callbacks to override the default SSL server certificate validation 553 * algorithm for the current certificate or the entire certificate chain. 554 */ 555 void serf_ssl_server_cert_chain_callback_set( 556 serf_ssl_context_t *context, 557 serf_ssl_need_server_cert_t cert_callback, 558 serf_ssl_server_cert_chain_cb_t cert_chain_callback, 559 void *data); 560 561 /** 562 * Use the default root CA certificates as included with the OpenSSL library. 563 */ 564 apr_status_t serf_ssl_use_default_certificates( 565 serf_ssl_context_t *context); 566 567 /** 568 * Allow SNI indicators to be sent to the server. 569 */ 570 apr_status_t serf_ssl_set_hostname( 571 serf_ssl_context_t *context, const char *hostname); 572 573 /** 574 * Return the depth of the certificate. 575 */ 576 int serf_ssl_cert_depth( 577 const serf_ssl_certificate_t *cert); 578 579 /** 580 * Extract the fields of the issuer in a table with keys (E, CN, OU, O, L, 581 * ST and C). The returned table will be allocated in @a pool. 582 */ 583 apr_hash_t *serf_ssl_cert_issuer( 584 const serf_ssl_certificate_t *cert, 585 apr_pool_t *pool); 586 587 /** 588 * Extract the fields of the subject in a table with keys (E, CN, OU, O, L, 589 * ST and C). The returned table will be allocated in @a pool. 590 */ 591 apr_hash_t *serf_ssl_cert_subject( 592 const serf_ssl_certificate_t *cert, 593 apr_pool_t *pool); 594 595 /** 596 * Extract the fields of the certificate in a table with keys (sha1, notBefore, 597 * notAfter, subjectAltName). The returned table will be allocated in @a pool. 598 */ 599 apr_hash_t *serf_ssl_cert_certificate( 600 const serf_ssl_certificate_t *cert, 601 apr_pool_t *pool); 602 603 /** 604 * Export a certificate to base64-encoded, zero-terminated string. 605 * The returned string is allocated in @a pool. Returns NULL on failure. 606 */ 607 const char *serf_ssl_cert_export( 608 const serf_ssl_certificate_t *cert, 609 apr_pool_t *pool); 610 611 /** 612 * Load a CA certificate file from a path @a file_path. If the file was loaded 613 * and parsed correctly, a certificate @a cert will be created and returned. 614 * This certificate object will be alloced in @a pool. 615 */ 616 apr_status_t serf_ssl_load_cert_file( 617 serf_ssl_certificate_t **cert, 618 const char *file_path, 619 apr_pool_t *pool); 620 621 /** 622 * Adds the certificate @a cert to the list of trusted certificates in 623 * @a ssl_ctx that will be used for verification. 624 * See also @a serf_ssl_load_cert_file. 625 */ 626 apr_status_t serf_ssl_trust_cert( 627 serf_ssl_context_t *ssl_ctx, 628 serf_ssl_certificate_t *cert); 629 630 /** 631 * Enable or disable SSL compression on a SSL session. 632 * @a enabled = 1 to enable compression, 0 to disable compression. 633 * Default = disabled. 634 */ 635 apr_status_t serf_ssl_use_compression( 636 serf_ssl_context_t *ssl_ctx, 637 int enabled); 638 639 serf_bucket_t *serf_bucket_ssl_encrypt_create( 640 serf_bucket_t *stream, 641 serf_ssl_context_t *ssl_context, 642 serf_bucket_alloc_t *allocator); 643 644 serf_ssl_context_t *serf_bucket_ssl_encrypt_context_get( 645 serf_bucket_t *bucket); 646 647 /* ==================================================================== */ 648 649 650 extern const serf_bucket_type_t serf_bucket_type_ssl_decrypt; 651 #define SERF_BUCKET_IS_SSL_DECRYPT(b) SERF_BUCKET_CHECK((b), ssl_decrypt) 652 653 serf_bucket_t *serf_bucket_ssl_decrypt_create( 654 serf_bucket_t *stream, 655 serf_ssl_context_t *ssl_context, 656 serf_bucket_alloc_t *allocator); 657 658 serf_ssl_context_t *serf_bucket_ssl_decrypt_context_get( 659 serf_bucket_t *bucket); 660 661 662 /* ==================================================================== */ 663 664 665 extern const serf_bucket_type_t serf_bucket_type_barrier; 666 #define SERF_BUCKET_IS_BARRIER(b) SERF_BUCKET_CHECK((b), barrier) 667 668 serf_bucket_t *serf_bucket_barrier_create( 669 serf_bucket_t *stream, 670 serf_bucket_alloc_t *allocator); 671 672 673 /* ==================================================================== */ 674 675 extern const serf_bucket_type_t serf_bucket_type_iovec; 676 #define SERF_BUCKET_IS_IOVEC(b) SERF_BUCKET_CHECK((b), iovec) 677 678 serf_bucket_t *serf_bucket_iovec_create( 679 struct iovec vecs[], 680 int len, 681 serf_bucket_alloc_t *allocator); 682 683 684 /* ==================================================================== */ 685 686 /* ### do we need a PIPE bucket type? they are simple apr_file_t objects */ 687 688 689 #ifdef __cplusplus 690 } 691 #endif 692 693 #endif /* !SERF_BUCKET_TYPES_H */ 694