1333347Speter/* 2333347Speter * request_body.c : svn_ra_serf__request_body_t implementation 3333347Speter * 4333347Speter * ==================================================================== 5333347Speter * Licensed to the Apache Software Foundation (ASF) under one 6333347Speter * or more contributor license agreements. See the NOTICE file 7333347Speter * distributed with this work for additional information 8333347Speter * regarding copyright ownership. The ASF licenses this file 9333347Speter * to you under the Apache License, Version 2.0 (the 10333347Speter * "License"); you may not use this file except in compliance 11333347Speter * with the License. You may obtain a copy of the License at 12333347Speter * 13333347Speter * http://www.apache.org/licenses/LICENSE-2.0 14333347Speter * 15333347Speter * Unless required by applicable law or agreed to in writing, 16333347Speter * software distributed under the License is distributed on an 17333347Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18333347Speter * KIND, either express or implied. See the License for the 19333347Speter * specific language governing permissions and limitations 20333347Speter * under the License. 21333347Speter * ==================================================================== 22333347Speter */ 23333347Speter 24333347Speter#include <serf.h> 25333347Speter 26333347Speter#include "ra_serf.h" 27333347Speter 28333347Speterstruct svn_ra_serf__request_body_t 29333347Speter{ 30333347Speter svn_stream_t *stream; 31333347Speter apr_size_t in_memory_size; 32333347Speter apr_size_t total_bytes; 33333347Speter serf_bucket_alloc_t *alloc; 34333347Speter serf_bucket_t *collect_bucket; 35333347Speter const void *all_data; 36333347Speter apr_file_t *file; 37333347Speter apr_pool_t *result_pool; 38333347Speter apr_pool_t *scratch_pool; 39333347Speter}; 40333347Speter 41333347Speter/* Fold all previously collected data in a single buffer allocated in 42333347Speter RESULT_POOL and clear all intermediate state. */ 43333347Speterstatic const char * 44333347Speterallocate_all(svn_ra_serf__request_body_t *body, 45333347Speter apr_pool_t *result_pool) 46333347Speter{ 47333347Speter char *buffer = apr_pcalloc(result_pool, body->total_bytes); 48333347Speter const char *data; 49333347Speter apr_size_t sz; 50333347Speter apr_status_t s; 51333347Speter apr_size_t remaining = body->total_bytes; 52333347Speter char *next = buffer; 53333347Speter 54333347Speter while (!(s = serf_bucket_read(body->collect_bucket, remaining, &data, &sz))) 55333347Speter { 56333347Speter memcpy(next, data, sz); 57333347Speter remaining -= sz; 58333347Speter next += sz; 59333347Speter 60333347Speter if (! remaining) 61333347Speter break; 62333347Speter } 63333347Speter 64333347Speter if (!SERF_BUCKET_READ_ERROR(s)) 65333347Speter { 66333347Speter memcpy(next, data, sz); 67333347Speter } 68333347Speter 69333347Speter serf_bucket_destroy(body->collect_bucket); 70333347Speter body->collect_bucket = NULL; 71333347Speter 72333347Speter return (s != APR_EOF) ? NULL : buffer; 73333347Speter} 74333347Speter 75333347Speter/* Noop function. Make serf take care of freeing in error situations. */ 76333347Speterstatic void serf_free_no_error(void *unfreed_baton, void *block) {} 77333347Speter 78333347Speter/* Stream write function for body creation. */ 79333347Speterstatic svn_error_t * 80333347Speterrequest_body_stream_write(void *baton, 81333347Speter const char *data, 82333347Speter apr_size_t *len) 83333347Speter{ 84333347Speter svn_ra_serf__request_body_t *b = baton; 85333347Speter 86333347Speter if (!b->scratch_pool) 87333347Speter b->scratch_pool = svn_pool_create(b->result_pool); 88333347Speter 89333347Speter if (b->file) 90333347Speter { 91333347Speter SVN_ERR(svn_io_file_write_full(b->file, data, *len, NULL, 92333347Speter b->scratch_pool)); 93333347Speter svn_pool_clear(b->scratch_pool); 94333347Speter 95333347Speter b->total_bytes += *len; 96333347Speter } 97333347Speter else if (*len + b->total_bytes > b->in_memory_size) 98333347Speter { 99333347Speter SVN_ERR(svn_io_open_unique_file3(&b->file, NULL, NULL, 100333347Speter svn_io_file_del_on_pool_cleanup, 101333347Speter b->result_pool, b->scratch_pool)); 102333347Speter 103333347Speter if (b->total_bytes) 104333347Speter { 105333347Speter const char *all = allocate_all(b, b->scratch_pool); 106333347Speter 107333347Speter SVN_ERR(svn_io_file_write_full(b->file, all, b->total_bytes, 108333347Speter NULL, b->scratch_pool)); 109333347Speter } 110333347Speter 111333347Speter SVN_ERR(svn_io_file_write_full(b->file, data, *len, NULL, 112333347Speter b->scratch_pool)); 113333347Speter b->total_bytes += *len; 114333347Speter } 115333347Speter else 116333347Speter { 117333347Speter if (!b->alloc) 118333347Speter b->alloc = serf_bucket_allocator_create(b->scratch_pool, 119333347Speter serf_free_no_error, NULL); 120333347Speter 121333347Speter if (!b->collect_bucket) 122333347Speter b->collect_bucket = serf_bucket_aggregate_create(b->alloc); 123333347Speter 124333347Speter serf_bucket_aggregate_append(b->collect_bucket, 125333347Speter serf_bucket_simple_copy_create(data, *len, 126333347Speter b->alloc)); 127333347Speter 128333347Speter b->total_bytes += *len; 129333347Speter } 130333347Speter 131333347Speter return SVN_NO_ERROR; 132333347Speter} 133333347Speter 134333347Speter/* Stream close function for collecting body. */ 135333347Speterstatic svn_error_t * 136333347Speterrequest_body_stream_close(void *baton) 137333347Speter{ 138333347Speter svn_ra_serf__request_body_t *b = baton; 139333347Speter 140333347Speter if (b->file) 141333347Speter { 142333347Speter /* We need to flush the file, make it unbuffered (so that it can be 143333347Speter * zero-copied via mmap), and reset the position before attempting 144333347Speter * to deliver the file. 145333347Speter * 146333347Speter * N.B. If we have APR 1.3+, we can unbuffer the file to let us use 147333347Speter * mmap and zero-copy the PUT body. However, on older APR versions, 148333347Speter * we can't check the buffer status; but serf will fall through and 149333347Speter * create a file bucket for us on the buffered handle. 150333347Speter */ 151333347Speter 152333347Speter SVN_ERR(svn_io_file_flush(b->file, b->scratch_pool)); 153333347Speter apr_file_buffer_set(b->file, NULL, 0); 154333347Speter } 155333347Speter else if (b->collect_bucket) 156333347Speter b->all_data = allocate_all(b, b->result_pool); 157333347Speter 158333347Speter if (b->scratch_pool) 159333347Speter svn_pool_destroy(b->scratch_pool); 160333347Speter 161333347Speter return SVN_NO_ERROR; 162333347Speter} 163333347Speter 164333347Speter/* Implements svn_ra_serf__request_body_delegate_t. */ 165333347Speterstatic svn_error_t * 166333347Speterrequest_body_delegate(serf_bucket_t **body_bkt, 167333347Speter void *baton, 168333347Speter serf_bucket_alloc_t *alloc, 169333347Speter apr_pool_t *request_pool, 170333347Speter apr_pool_t *scratch_pool) 171333347Speter{ 172333347Speter svn_ra_serf__request_body_t *b = baton; 173333347Speter 174333347Speter if (b->file) 175333347Speter { 176333347Speter apr_off_t offset; 177333347Speter 178333347Speter offset = 0; 179333347Speter SVN_ERR(svn_io_file_seek(b->file, APR_SET, &offset, scratch_pool)); 180333347Speter 181333347Speter *body_bkt = serf_bucket_file_create(b->file, alloc); 182333347Speter } 183333347Speter else 184333347Speter { 185333347Speter *body_bkt = serf_bucket_simple_create(b->all_data, 186333347Speter b->total_bytes, 187333347Speter NULL, NULL, alloc); 188333347Speter } 189333347Speter 190333347Speter return SVN_NO_ERROR; 191333347Speter} 192333347Speter 193333347Spetersvn_ra_serf__request_body_t * 194333347Spetersvn_ra_serf__request_body_create(apr_size_t in_memory_size, 195333347Speter apr_pool_t *result_pool) 196333347Speter{ 197333347Speter svn_ra_serf__request_body_t *body = apr_pcalloc(result_pool, sizeof(*body)); 198333347Speter 199333347Speter body->in_memory_size = in_memory_size; 200333347Speter body->result_pool = result_pool; 201333347Speter body->stream = svn_stream_create(body, result_pool); 202333347Speter 203333347Speter svn_stream_set_write(body->stream, request_body_stream_write); 204333347Speter svn_stream_set_close(body->stream, request_body_stream_close); 205333347Speter 206333347Speter return body; 207333347Speter} 208333347Speter 209333347Spetersvn_stream_t * 210333347Spetersvn_ra_serf__request_body_get_stream(svn_ra_serf__request_body_t *body) 211333347Speter{ 212333347Speter return body->stream; 213333347Speter} 214333347Speter 215333347Spetervoid 216333347Spetersvn_ra_serf__request_body_get_delegate(svn_ra_serf__request_body_delegate_t *del, 217333347Speter void **baton, 218333347Speter svn_ra_serf__request_body_t *body) 219333347Speter{ 220333347Speter *del = request_body_delegate; 221333347Speter *baton = body; 222333347Speter} 223333347Speter 224333347Spetersvn_error_t * 225333347Spetersvn_ra_serf__request_body_cleanup(svn_ra_serf__request_body_t *body, 226333347Speter apr_pool_t *scratch_pool) 227333347Speter{ 228333347Speter if (body->file) 229333347Speter SVN_ERR(svn_io_file_close(body->file, scratch_pool)); 230333347Speter 231333347Speter return SVN_NO_ERROR; 232333347Speter} 233