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