char *serf_bstrcatv(serf_bucket_alloc_t *allocator, struct iovec *vec, int vecs, apr_size_t *bytes_written) { int i; apr_size_t new_len = 0; char *c, *newstr; for (i = 0; i < vecs; i++) { new_len += vec[i].iov_len; } /* It's up to the caller to free this memory later. */ newstr = serf_bucket_mem_alloc(allocator, new_len); c = newstr; for (i = 0; i < vecs; i++) { memcpy(c, vec[i].iov_base, vec[i].iov_len); c += vec[i].iov_len; } if (bytes_written) { *bytes_written = c - newstr; } return newstr; }
void serf_bucket_aggregate_append( serf_bucket_t *aggregate_bucket, serf_bucket_t *append_bucket) { aggregate_context_t *ctx = aggregate_bucket->data; bucket_list_t *new_list; new_list = serf_bucket_mem_alloc(aggregate_bucket->allocator, sizeof(*new_list)); new_list->bucket = append_bucket; new_list->next = NULL; /* If we use APR_RING, this is trivial. So, wait. new_list->next = ctx->list; ctx->list = new_list; */ if (ctx->list == NULL) { ctx->list = new_list; ctx->last = new_list; } else { ctx->last->next = new_list; ctx->last = ctx->last->next; } }
char *serf_bstrdup(serf_bucket_alloc_t *allocator, const char *str) { apr_size_t size = strlen(str) + 1; char *newstr = serf_bucket_mem_alloc(allocator, size); memcpy(newstr, str, size); return newstr; }
void *serf_bmemdup(serf_bucket_alloc_t *allocator, const void *mem, apr_size_t size) { void *newmem = serf_bucket_mem_alloc(allocator, size); memcpy(newmem, mem, size); return newmem; }
SERF_DECLARE(serf_bucket_t *) serf_bucket_simple_copy_create( const char *data, apr_size_t len, serf_bucket_alloc_t *allocator) { simple_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->original = ctx->current = serf_bucket_mem_alloc(allocator, len); memcpy((char*)ctx->original, data, len); ctx->remaining = len; ctx->freefunc = free_copied_data; ctx->baton = allocator; return serf_bucket_create(&serf_bucket_type_simple, allocator, ctx); }
char *serf_bstrmemdup(serf_bucket_alloc_t *allocator, const char *str, apr_size_t size) { char *newstr = serf_bucket_mem_alloc(allocator, size + 1); memcpy(newstr, str, size); newstr[size] = '\0'; return newstr; }
static serf_ssl_context_t *ssl_init_context(void) { serf_ssl_context_t *ssl_ctx; apr_pool_t *pool; serf_bucket_alloc_t *allocator; init_ssl_libraries(); apr_pool_create(&pool, NULL); allocator = serf_bucket_allocator_create(pool, NULL, NULL); ssl_ctx = serf_bucket_mem_alloc(allocator, sizeof(*ssl_ctx)); ssl_ctx->refcount = 0; ssl_ctx->pool = pool; ssl_ctx->allocator = allocator; ssl_ctx->ctx = SSL_CTX_new(SSLv23_client_method()); SSL_CTX_set_client_cert_cb(ssl_ctx->ctx, ssl_need_client_cert); ssl_ctx->cached_cert = 0; ssl_ctx->cached_cert_pw = 0; ssl_ctx->pending_err = APR_SUCCESS; SSL_CTX_set_verify(ssl_ctx->ctx, SSL_VERIFY_PEER, validate_server_certificate); SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_ALL); ssl_ctx->ssl = SSL_new(ssl_ctx->ctx); ssl_ctx->bio = BIO_new(&bio_bucket_method); ssl_ctx->bio->ptr = ssl_ctx; SSL_set_bio(ssl_ctx->ssl, ssl_ctx->bio, ssl_ctx->bio); SSL_set_connect_state(ssl_ctx->ssl); SSL_set_app_data(ssl_ctx->ssl, ssl_ctx); ssl_ctx->encrypt.stream = NULL; ssl_ctx->encrypt.stream_next = NULL; ssl_ctx->encrypt.pending = serf_bucket_aggregate_create(allocator); ssl_ctx->encrypt.status = APR_SUCCESS; serf_databuf_init(&ssl_ctx->encrypt.databuf); ssl_ctx->encrypt.databuf.read = ssl_encrypt; ssl_ctx->encrypt.databuf.read_baton = ssl_ctx; ssl_ctx->decrypt.stream = NULL; ssl_ctx->decrypt.pending = serf_bucket_aggregate_create(allocator); ssl_ctx->decrypt.status = APR_SUCCESS; serf_databuf_init(&ssl_ctx->decrypt.databuf); ssl_ctx->decrypt.databuf.read = ssl_decrypt; ssl_ctx->decrypt.databuf.read_baton = ssl_ctx; return ssl_ctx; }
serf_bucket_t *serf_bucket_headers_create( serf_bucket_alloc_t *allocator) { headers_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->list = NULL; ctx->state = READ_START; return serf_bucket_create(&serf_bucket_type_headers, allocator, ctx); }
SERF_DECLARE(serf_bucket_t *) serf_bucket_barrier_create( serf_bucket_t *stream, serf_bucket_alloc_t *allocator) { barrier_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->stream = stream; return serf_bucket_create(&serf_bucket_type_barrier, allocator, ctx); }
serf_bucket_t *serf_bucket_limit_create( serf_bucket_t *stream, apr_uint64_t len, serf_bucket_alloc_t *allocator) { limit_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->stream = stream; ctx->remaining = len; return serf_bucket_create(&serf_bucket_type_limit, allocator, ctx); }
serf_bucket_t *serf_bucket_chunk_create( serf_bucket_t *stream, serf_bucket_alloc_t *allocator) { chunk_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->state = STATE_FETCH; ctx->chunk = serf_bucket_aggregate_create(allocator); ctx->stream = stream; return serf_bucket_create(&serf_bucket_type_chunk, allocator, ctx); }
serf_bucket_t *serf_bucket_create( const serf_bucket_type_t *type, serf_bucket_alloc_t *allocator, void *data) { serf_bucket_t *bkt = serf_bucket_mem_alloc(allocator, sizeof(*bkt)); bkt->type = type; bkt->data = data; bkt->allocator = allocator; return bkt; }
serf_bucket_t *serf_bucket_iovec_create( struct iovec vecs[], int len, serf_bucket_alloc_t *allocator) { iovec_context_t *ctx; int i; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->vecs = serf_bucket_mem_alloc(allocator, len * sizeof(struct iovec)); ctx->vecs_len = len; ctx->current_vec = 0; ctx->offset = 0; /* copy all buffers to our iovec. */ for (i = 0; i < len; i++) { ctx->vecs[i].iov_base = vecs[i].iov_base; ctx->vecs[i].iov_len = vecs[i].iov_len; } return serf_bucket_create(&serf_bucket_type_iovec, allocator, ctx); }
void serf_bucket_aggregate_prepend( serf_bucket_t *aggregate_bucket, serf_bucket_t *prepend_bucket) { aggregate_context_t *ctx = aggregate_bucket->data; bucket_list_t *new_list; new_list = serf_bucket_mem_alloc(aggregate_bucket->allocator, sizeof(*new_list)); new_list->bucket = prepend_bucket; new_list->next = ctx->list; ctx->list = new_list; }
SERF_DECLARE(serf_bucket_t *) serf_bucket_dechunk_create( serf_bucket_t *stream, serf_bucket_alloc_t *allocator) { dechunk_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->stream = stream; ctx->state = STATE_SIZE; serf_linebuf_init(&ctx->linebuf); return serf_bucket_create(&serf_bucket_type_dechunk, allocator, ctx); }
SERF_DECLARE(serf_bucket_t *) serf_bucket_mmap_create( apr_mmap_t *file_mmap, serf_bucket_alloc_t *allocator) { mmap_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->mmap = file_mmap; ctx->current = NULL; ctx->offset = 0; ctx->remaining = ctx->mmap->size; return serf_bucket_create(&serf_bucket_type_mmap, allocator, ctx); }
static aggregate_context_t *create_aggregate(serf_bucket_alloc_t *allocator) { aggregate_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->list = NULL; ctx->last = NULL; ctx->done = NULL; ctx->hold_open = NULL; ctx->hold_open_baton = NULL; ctx->bucket_owner = 1; return ctx; }
void serf_bucket_headers_setx( serf_bucket_t *bkt, const char *header, apr_size_t header_size, int header_copy, const char *value, apr_size_t value_size, int value_copy) { headers_context_t *ctx = bkt->data; header_list_t *iter = ctx->list; header_list_t *hdr; #if 0 /* ### include this? */ if (ctx->cur_read) { /* we started reading. can't change now. */ abort(); } #endif hdr = serf_bucket_mem_alloc(bkt->allocator, sizeof(*hdr)); hdr->header_size = header_size; hdr->value_size = value_size; hdr->alloc_flags = 0; hdr->next = NULL; if (header_copy) { hdr->header = serf_bstrmemdup(bkt->allocator, header, header_size); hdr->alloc_flags |= ALLOC_HEADER; } else { hdr->header = header; } if (value_copy) { hdr->value = serf_bstrmemdup(bkt->allocator, value, value_size); hdr->alloc_flags |= ALLOC_VALUE; } else { hdr->value = value; } /* Add the new header at the end of the list. */ while (iter && iter->next) { iter = iter->next; } if (iter) iter->next = hdr; else ctx->list = hdr; }
serf_bucket_t *serf_bucket_mock_create(mockbkt_action *actions, int len, serf_bucket_alloc_t *allocator) { mockbkt_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->actions = actions; ctx->len = len; ctx->current_data = 0l; ctx->remaining_data = -1; ctx->current_action = 0; ctx->remaining_times = -1; return serf_bucket_create(&serf_bucket_type_mock, allocator, ctx); }
serf_bucket_t *serf_bucket_simple_create( const char *data, apr_size_t len, serf_simple_freefunc_t freefunc, void *freefunc_baton, serf_bucket_alloc_t *allocator) { simple_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->original = ctx->current = data; ctx->remaining = len; ctx->freefunc = freefunc; ctx->baton = freefunc_baton; return serf_bucket_create(&serf_bucket_type_simple, allocator, ctx); }
SERF_DECLARE(serf_bucket_t *) serf_bucket_socket_create( apr_socket_t *skt, serf_bucket_alloc_t *allocator) { socket_context_t *ctx; /* Oh, well. */ ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->skt = skt; serf_databuf_init(&ctx->databuf); ctx->databuf.read = socket_reader; ctx->databuf.read_baton = ctx; ctx->progress_func = ctx->progress_baton = NULL; return serf_bucket_create(&serf_bucket_type_socket, allocator, ctx); }
const char *serf_bucket_headers_get( serf_bucket_t *headers_bucket, const char *header) { headers_context_t *ctx = headers_bucket->data; header_list_t *found = ctx->list; const char *val = NULL; int value_size = 0; int val_alloc = 0; while (found) { if (strcasecmp(found->header, header) == 0) { if (val) { /* The header is already present. RFC 2616, section 4.2 indicates that we should append the new value, separated by a comma. Reasoning: for headers whose values are known to be comma-separated, that is clearly the correct behavior; for others, the correct behavior is undefined anyway. */ /* The "+1" is for the comma; serf_bstrmemdup() will also add one slot for the terminating '\0'. */ apr_size_t new_size = found->value_size + value_size + 1; char *new_val = serf_bucket_mem_alloc(headers_bucket->allocator, new_size); memcpy(new_val, val, value_size); new_val[value_size] = ','; memcpy(new_val + value_size + 1, found->value, found->value_size); new_val[new_size] = '\0'; /* Copy the new value over the already existing value. */ if (val_alloc) serf_bucket_mem_free(headers_bucket->allocator, (void*)val); val_alloc |= ALLOC_VALUE; val = new_val; value_size = new_size; } else { val = found->value; value_size = found->value_size; } } found = found->next; } return val; }
serf_bucket_t *serf_bucket_response_create( serf_bucket_t *stream, serf_bucket_alloc_t *allocator) { response_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->stream = stream; ctx->body = NULL; ctx->headers = serf_bucket_headers_create(allocator); ctx->state = STATE_STATUS_LINE; ctx->chunked = 0; ctx->head_req = 0; serf_linebuf_init(&ctx->linebuf); return serf_bucket_create(&serf_bucket_type_response, allocator, ctx); }
static serf_bucket_t * serf_bucket_ssl_create( serf_ssl_context_t *ssl_ctx, serf_bucket_alloc_t *allocator, const serf_bucket_type_t *type) { ssl_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); if (!ssl_ctx) { ctx->ssl_ctx = ssl_init_context(); } else { ctx->ssl_ctx = ssl_ctx; } ctx->ssl_ctx->refcount++; return serf_bucket_create(type, allocator, ctx); }
serf_bucket_t *serf_bucket_ssl_encrypt_create( serf_bucket_t *stream, serf_ssl_context_t *ssl_ctx, serf_bucket_alloc_t *allocator) { serf_bucket_t *bkt; ssl_context_t *ctx; bkt = serf_bucket_ssl_create(ssl_ctx, allocator, &serf_bucket_type_ssl_encrypt); ctx = bkt->data; ctx->databuf = &ctx->ssl_ctx->encrypt.databuf; ctx->our_stream = &ctx->ssl_ctx->encrypt.stream; if (ctx->ssl_ctx->encrypt.stream == NULL) { serf_bucket_t *tmp = serf_bucket_aggregate_create(stream->allocator); serf_bucket_aggregate_append(tmp, stream); ctx->ssl_ctx->encrypt.stream = tmp; } else { bucket_list_t *new_list; new_list = serf_bucket_mem_alloc(ctx->ssl_ctx->allocator, sizeof(*new_list)); new_list->bucket = stream; new_list->next = NULL; if (ctx->ssl_ctx->encrypt.stream_next == NULL) { ctx->ssl_ctx->encrypt.stream_next = new_list; } else { bucket_list_t *scan = ctx->ssl_ctx->encrypt.stream_next; while (scan->next != NULL) scan = scan->next; scan->next = new_list; } } return bkt; }
serf_bucket_t *serf_bucket_deflate_create( serf_bucket_t *stream, serf_bucket_alloc_t *allocator, int format) { deflate_context_t *ctx; ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->stream = stream; ctx->stream_status = APR_SUCCESS; ctx->inflate_stream = serf_bucket_aggregate_create(allocator); ctx->format = format; ctx->crc = 0; ctx->config = NULL; /* zstream must be NULL'd out. */ memset(&ctx->zstream, 0, sizeof(ctx->zstream)); switch (ctx->format) { case SERF_DEFLATE_GZIP: ctx->state = STATE_READING_HEADER; break; case SERF_DEFLATE_DEFLATE: /* deflate doesn't have a header. */ ctx->state = STATE_INIT; break; default: /* Not reachable */ return NULL; } /* Initial size of gzip header. */ ctx->stream_left = ctx->stream_size = DEFLATE_MAGIC_SIZE; ctx->windowSize = DEFLATE_WINDOW_SIZE; ctx->memLevel = DEFLATE_MEMLEVEL; ctx->bufferSize = DEFLATE_BUFFER_SIZE; return serf_bucket_create(&serf_bucket_type_deflate, allocator, ctx); }
int main(int argc, const char **argv) { apr_status_t status; apr_pool_t *pool; apr_sockaddr_t *address; serf_context_t *context; serf_connection_t *connection; app_baton_t app_ctx; handler_baton_t *handler_ctx; apr_uri_t url; const char *raw_url, *method; int count; apr_getopt_t *opt; char opt_c; char *authn = NULL; const char *opt_arg; /* For the parser threads */ apr_thread_t *thread[3]; apr_threadattr_t *tattr; apr_status_t parser_status; parser_baton_t *parser_ctx; apr_initialize(); atexit(apr_terminate); apr_pool_create(&pool, NULL); apr_atomic_init(pool); /* serf_initialize(); */ /* Default to one round of fetching. */ count = 1; /* Default to GET. */ method = "GET"; apr_getopt_init(&opt, pool, argc, argv); while ((status = apr_getopt(opt, "a:hv", &opt_c, &opt_arg)) == APR_SUCCESS) { int srclen, enclen; switch (opt_c) { case 'a': srclen = strlen(opt_arg); enclen = apr_base64_encode_len(srclen); authn = apr_palloc(pool, enclen + 6); strcpy(authn, "Basic "); (void) apr_base64_encode(&authn[6], opt_arg, srclen); break; case 'h': print_usage(pool); exit(0); break; case 'v': puts("Serf version: " SERF_VERSION_STRING); exit(0); default: break; } } if (opt->ind != opt->argc - 1) { print_usage(pool); exit(-1); } raw_url = argv[opt->ind]; apr_uri_parse(pool, raw_url, &url); if (!url.port) { url.port = apr_uri_port_of_scheme(url.scheme); } if (!url.path) { url.path = "/"; } if (strcasecmp(url.scheme, "https") == 0) { app_ctx.using_ssl = 1; } else { app_ctx.using_ssl = 0; } status = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool); if (status) { printf("Error creating address: %d\n", status); exit(1); } context = serf_context_create(pool); /* ### Connection or Context should have an allocator? */ app_ctx.bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL); app_ctx.ssl_ctx = NULL; app_ctx.authn = authn; connection = serf_connection_create(context, address, conn_setup, &app_ctx, closed_connection, &app_ctx, pool); handler_ctx = (handler_baton_t*)serf_bucket_mem_alloc(app_ctx.bkt_alloc, sizeof(handler_baton_t)); handler_ctx->allocator = app_ctx.bkt_alloc; handler_ctx->doc_queue = apr_array_make(pool, 1, sizeof(doc_path_t*)); handler_ctx->doc_queue_alloc = app_ctx.bkt_alloc; handler_ctx->requests_outstanding = (apr_uint32_t*)serf_bucket_mem_alloc(app_ctx.bkt_alloc, sizeof(apr_uint32_t)); apr_atomic_set32(handler_ctx->requests_outstanding, 0); handler_ctx->hdr_read = 0; parser_ctx = (void*)serf_bucket_mem_alloc(app_ctx.bkt_alloc, sizeof(parser_baton_t)); parser_ctx->requests_outstanding = handler_ctx->requests_outstanding; parser_ctx->connection = connection; parser_ctx->app_ctx = &app_ctx; parser_ctx->doc_queue = handler_ctx->doc_queue; parser_ctx->doc_queue_alloc = handler_ctx->doc_queue_alloc; /* Restrict ourselves to this host. */ parser_ctx->hostinfo = url.hostinfo; status = apr_thread_mutex_create(&parser_ctx->mutex, APR_THREAD_MUTEX_DEFAULT, pool); if (status) { printf("Couldn't create mutex %d\n", status); return status; } status = apr_thread_cond_create(&parser_ctx->condvar, pool); if (status) { printf("Couldn't create condvar: %d\n", status); return status; } /* Let the handler now which condvar to use. */ handler_ctx->doc_queue_condvar = parser_ctx->condvar; apr_threadattr_create(&tattr, pool); /* Start the parser thread. */ apr_thread_create(&thread[0], tattr, parser_thread, parser_ctx, pool); /* Deliver the first request. */ create_request(url.hostinfo, url.path, NULL, NULL, parser_ctx, pool); /* Go run our normal thread. */ while (1) { int tries = 0; status = serf_context_run(context, SERF_DURATION_FOREVER, pool); if (APR_STATUS_IS_TIMEUP(status)) continue; if (status) { char buf[200]; printf("Error running context: (%d) %s\n", status, apr_strerror(status, buf, sizeof(buf))); exit(1); } /* We run this check to allow our parser threads to add more * requests to our queue. */ for (tries = 0; tries < 3; tries++) { if (!apr_atomic_read32(handler_ctx->requests_outstanding)) { #ifdef SERF_VERBOSE printf("Waiting..."); #endif apr_sleep(100000); #ifdef SERF_VERBOSE printf("Done\n"); #endif } else { break; } } if (tries >= 3) { break; } /* Debugging purposes only! */ serf_debug__closed_conn(app_ctx.bkt_alloc); } printf("Quitting...\n"); serf_connection_close(connection); /* wake up the parser via condvar signal */ apr_thread_cond_signal(parser_ctx->condvar); status = apr_thread_join(&parser_status, thread[0]); if (status) { printf("Error joining thread: %d\n", status); return status; } serf_bucket_mem_free(app_ctx.bkt_alloc, handler_ctx->requests_outstanding); serf_bucket_mem_free(app_ctx.bkt_alloc, parser_ctx); apr_pool_destroy(pool); return 0; }
static apr_status_t create_request(const char *hostinfo, const char *path, const char *query, const char *fragment, parser_baton_t *ctx, apr_pool_t *tmppool) { handler_baton_t *new_ctx; if (hostinfo) { /* Yes, this is a pointer comparison; not a string comparison. */ if (hostinfo != ctx->hostinfo) { /* Not on the same host; ignore */ return APR_SUCCESS; } } new_ctx = (handler_baton_t*)serf_bucket_mem_alloc(ctx->app_ctx->bkt_alloc, sizeof(handler_baton_t)); new_ctx->allocator = ctx->app_ctx->bkt_alloc; new_ctx->requests_outstanding = ctx->requests_outstanding; new_ctx->app_ctx = ctx->app_ctx; /* See above: this example restricts ourselves to the same vhost. */ new_ctx->hostinfo = ctx->hostinfo; /* we need to copy it so it falls under the request's scope. */ new_ctx->path_len = strlen(path); new_ctx->path = (char*)serf_bucket_mem_alloc(ctx->app_ctx->bkt_alloc, new_ctx->path_len + 1); memcpy(new_ctx->path, path, new_ctx->path_len + 1); /* we need to copy it so it falls under the request's scope. */ if (query) { new_ctx->query_len = strlen(query); new_ctx->query = (char*)serf_bucket_mem_alloc(ctx->app_ctx->bkt_alloc, new_ctx->query_len + 1); memcpy(new_ctx->query, query, new_ctx->query_len + 1); } else { new_ctx->query = NULL; new_ctx->query_len = 0; } /* we need to copy it so it falls under the request's scope. */ if (fragment) { new_ctx->fragment_len = strlen(fragment); new_ctx->fragment = (char*)serf_bucket_mem_alloc(ctx->app_ctx->bkt_alloc, new_ctx->fragment_len + 1); memcpy(new_ctx->fragment, fragment, new_ctx->fragment_len + 1); } else { new_ctx->fragment = NULL; new_ctx->fragment_len = 0; } if (!new_ctx->query) { new_ctx->full_path = new_ctx->path; new_ctx->full_path_len = new_ctx->path_len; } else { new_ctx->full_path_len = new_ctx->path_len + new_ctx->query_len; new_ctx->full_path = (char*)serf_bucket_mem_alloc(ctx->app_ctx->bkt_alloc, new_ctx->full_path_len + 1); memcpy(new_ctx->full_path, new_ctx->path, new_ctx->path_len); memcpy(new_ctx->full_path + new_ctx->path_len, new_ctx->query, new_ctx->query_len + 1); } new_ctx->hdr_read = 0; new_ctx->doc_queue_condvar = ctx->condvar; new_ctx->doc_queue = ctx->doc_queue; new_ctx->doc_queue_alloc = ctx->doc_queue_alloc; new_ctx->acceptor = accept_response; new_ctx->acceptor_baton = &ctx->app_ctx; new_ctx->handler = handle_response; apr_atomic_inc32(ctx->requests_outstanding); serf_connection_request_create(ctx->connection, setup_request, new_ctx); return APR_SUCCESS; }
static apr_status_t handle_response(serf_request_t *request, serf_bucket_t *response, void *handler_baton, apr_pool_t *pool) { const char *data; apr_size_t len; serf_status_line sl; apr_status_t status; handler_baton_t *ctx = handler_baton; if (!response) { /* Oh no! We've been cancelled! */ abort(); } status = serf_bucket_response_status(response, &sl); if (status) { if (APR_STATUS_IS_EAGAIN(status)) { return APR_SUCCESS; } abort(); } while (1) { status = serf_bucket_read(response, 2048, &data, &len); if (SERF_BUCKET_READ_ERROR(status)) return status; /*fwrite(data, 1, len, stdout);*/ if (!ctx->hdr_read) { serf_bucket_t *hdrs; const char *val; printf("Processing %s\n", ctx->path); hdrs = serf_bucket_response_get_headers(response); val = serf_bucket_headers_get(hdrs, "Content-Type"); /* FIXME: This check isn't quite right because Content-Type could * be decorated; ideally strcasestr would be correct. */ if (val && strcasecmp(val, "text/html") == 0) { ctx->is_html = 1; apr_pool_create(&ctx->parser_pool, NULL); ctx->parser = apr_xml_parser_create(ctx->parser_pool); } else { ctx->is_html = 0; } ctx->hdr_read = 1; } if (ctx->is_html) { apr_status_t xs; xs = apr_xml_parser_feed(ctx->parser, data, len); /* Uh-oh. */ if (xs) { #ifdef SERF_VERBOSE printf("XML parser error (feed): %d\n", xs); #endif ctx->is_html = 0; } } /* are we done yet? */ if (APR_STATUS_IS_EOF(status)) { if (ctx->is_html) { apr_xml_doc *xmld; apr_status_t xs; doc_path_t *dup; xs = apr_xml_parser_done(ctx->parser, &xmld); if (xs) { #ifdef SERF_VERBOSE printf("XML parser error (done): %d\n", xs); #endif return xs; } dup = (doc_path_t*) serf_bucket_mem_alloc(ctx->doc_queue_alloc, sizeof(doc_path_t)); dup->doc = xmld; dup->path = (char*)serf_bucket_mem_alloc(ctx->doc_queue_alloc, ctx->path_len); memcpy(dup->path, ctx->path, ctx->path_len); dup->pool = ctx->parser_pool; *(doc_path_t **)apr_array_push(ctx->doc_queue) = dup; apr_thread_cond_signal(ctx->doc_queue_condvar); } apr_atomic_dec32(ctx->requests_outstanding); serf_bucket_mem_free(ctx->allocator, ctx->path); if (ctx->query) { serf_bucket_mem_free(ctx->allocator, ctx->query); serf_bucket_mem_free(ctx->allocator, ctx->full_path); } if (ctx->fragment) { serf_bucket_mem_free(ctx->allocator, ctx->fragment); } serf_bucket_mem_free(ctx->allocator, ctx); return APR_EOF; } /* have we drained the response so far? */ if (APR_STATUS_IS_EAGAIN(status)) return APR_SUCCESS; /* loop to read some more. */ } /* NOTREACHED */ }
/* This function reads a decrypted stream and returns an encrypted stream. */ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize, char *buf, apr_size_t *len) { const char *data; apr_size_t interim_bufsize; serf_ssl_context_t *ctx = baton; apr_status_t status; #ifdef SSL_VERBOSE printf("ssl_encrypt: begin %d\n", bufsize); #endif /* Try to read already encrypted but unread data first. */ status = serf_bucket_read(ctx->encrypt.pending, bufsize, &data, len); if (SERF_BUCKET_READ_ERROR(status)) { return status; } /* Aha, we read something. Return that now. */ if (*len) { memcpy(buf, data, *len); if (APR_STATUS_IS_EOF(status)) { status = APR_SUCCESS; } #ifdef SSL_VERBOSE printf("ssl_encrypt: %d %d %d (quick read)\n", status, *len, BIO_get_retry_flags(ctx->bio)); #endif return status; } if (BIO_should_retry(ctx->bio) && BIO_should_write(ctx->bio)) { #ifdef SSL_VERBOSE printf("ssl_encrypt: %d %d %d (should write exit)\n", status, *len, BIO_get_retry_flags(ctx->bio)); #endif return APR_EAGAIN; } /* If we were previously blocked, unblock ourselves now. */ if (BIO_should_read(ctx->bio)) { #ifdef SSL_VERBOSE printf("ssl_encrypt: reset %d %d (%d %d %d)\n", status, ctx->encrypt.status, BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), BIO_get_retry_flags(ctx->bio)); #endif ctx->encrypt.status = APR_SUCCESS; ctx->encrypt.exhausted_reset = 0; } /* Oh well, read from our stream now. */ interim_bufsize = bufsize; do { apr_size_t interim_len; if (!ctx->encrypt.status) { struct iovec vecs[64]; int vecs_read; status = serf_bucket_read_iovec(ctx->encrypt.stream, interim_bufsize, 64, vecs, &vecs_read); if (!SERF_BUCKET_READ_ERROR(status) && vecs_read) { char *vecs_data; int i, cur, vecs_data_len; int ssl_len; /* Combine the buffers of the iovec into one buffer, as that is with SSL_write requires. */ vecs_data_len = 0; for (i = 0; i < vecs_read; i++) { vecs_data_len += vecs[i].iov_len; } vecs_data = serf_bucket_mem_alloc(ctx->allocator, vecs_data_len); cur = 0; for (i = 0; i < vecs_read; i++) { memcpy(vecs_data + cur, vecs[i].iov_base, vecs[i].iov_len); cur += vecs[i].iov_len; } interim_bufsize -= vecs_data_len; interim_len = vecs_data_len; #ifdef SSL_VERBOSE printf("ssl_encrypt: bucket read %d bytes; status %d\n", interim_len, status); printf("---\n%s\n-(%d)-\n", vecs_data, interim_len); #endif /* Stash our status away. */ ctx->encrypt.status = status; ssl_len = SSL_write(ctx->ssl, vecs_data, interim_len); #ifdef SSL_VERBOSE printf("ssl_encrypt: SSL write: %d\n", ssl_len); #endif /* We're done. */ serf_bucket_mem_free(ctx->allocator, vecs_data); /* If we failed to write... */ if (ssl_len < 0) { int ssl_err; /* Ah, bugger. We need to put that data back. */ serf_bucket_aggregate_prepend_iovec(ctx->encrypt.stream, vecs, vecs_read); ssl_err = SSL_get_error(ctx->ssl, ssl_len); #ifdef SSL_VERBOSE printf("ssl_encrypt: SSL write error: %d\n", ssl_err); #endif if (ssl_err == SSL_ERROR_SYSCALL) { status = ctx->encrypt.status; if (SERF_BUCKET_READ_ERROR(status)) { return status; } } else { /* Oh, no. */ if (ssl_err == SSL_ERROR_WANT_READ) { status = SERF_ERROR_WAIT_CONN; } else { status = APR_EGENERAL; } } #ifdef SSL_VERBOSE printf("ssl_encrypt: SSL write error: %d %d\n", status, *len); #endif } } } else { interim_len = 0; *len = 0; status = ctx->encrypt.status; } } while (!status && interim_bufsize); /* Okay, we exhausted our underlying stream. */ if (!SERF_BUCKET_READ_ERROR(status)) { apr_status_t agg_status; struct iovec vecs[64]; int vecs_read, i; /* We read something! */ agg_status = serf_bucket_read_iovec(ctx->encrypt.pending, bufsize, 64, vecs, &vecs_read); *len = 0; for (i = 0; i < vecs_read; i++) { memcpy(buf + *len, vecs[i].iov_base, vecs[i].iov_len); *len += vecs[i].iov_len; } #ifdef SSL_VERBOSE printf("ssl_encrypt read agg: %d %d %d %d\n", status, agg_status, ctx->encrypt.status, *len); #endif if (!agg_status) { status = agg_status; } } if (status == SERF_ERROR_WAIT_CONN && BIO_should_retry(ctx->bio) && BIO_should_read(ctx->bio)) { ctx->encrypt.exhausted = ctx->encrypt.status; ctx->encrypt.status = SERF_ERROR_WAIT_CONN; } #ifdef SSL_VERBOSE printf("ssl_encrypt finished: %d %d (%d %d %d)\n", status, *len, BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), BIO_get_retry_flags(ctx->bio)); #endif return status; }