1/*
2 * sb_bucket.c :  a serf bucket that wraps a spillbuf
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24#include <serf.h>
25#include <serf_bucket_util.h>
26
27#include "svn_private_config.h"
28#include "private/svn_subr_private.h"
29
30#include "ra_serf.h"
31
32#define SB_BLOCKSIZE 1024
33#define SB_MAXSIZE 32768
34
35
36struct sbb_baton
37{
38  svn_spillbuf_t *spillbuf;
39
40  const char *holding;
41  apr_size_t hold_len;
42
43  apr_pool_t *scratch_pool;
44};
45
46
47svn_error_t *
48svn_ra_serf__copy_into_spillbuf(svn_spillbuf_t **spillbuf,
49                                serf_bucket_t *bkt,
50                                apr_pool_t *result_pool,
51                                apr_pool_t *scratch_pool)
52{
53  *spillbuf = svn_spillbuf__create(SB_BLOCKSIZE, SB_MAXSIZE, result_pool);
54
55  /* Copy all data from the bucket into the spillbuf.  */
56  while (TRUE)
57    {
58      apr_status_t status;
59      const char *data;
60      apr_size_t len;
61
62      status = serf_bucket_read(bkt, SERF_READ_ALL_AVAIL, &data, &len);
63
64      if (status != APR_SUCCESS && status != APR_EOF)
65        return svn_ra_serf__wrap_err(status, _("Failed to read the request"));
66
67      SVN_ERR(svn_spillbuf__write(*spillbuf, data, len, scratch_pool));
68
69      if (status == APR_EOF)
70        break;
71    }
72
73  return SVN_NO_ERROR;
74}
75
76
77static apr_status_t
78sb_bucket_read(serf_bucket_t *bucket, apr_size_t requested,
79               const char **data, apr_size_t *len)
80{
81  struct sbb_baton *sbb = bucket->data;
82  svn_error_t *err;
83
84  if (sbb->holding)
85    {
86      *data = sbb->holding;
87
88      if (requested < sbb->hold_len)
89        {
90          *len = requested;
91          sbb->holding += requested;
92          sbb->hold_len -= requested;
93          return APR_SUCCESS;
94        }
95
96      /* Return whatever we're holding, and then forget (consume) it.  */
97      *len = sbb->hold_len;
98      sbb->holding = NULL;
99      return APR_SUCCESS;
100    }
101
102  err = svn_spillbuf__read(data, len, sbb->spillbuf, sbb->scratch_pool);
103  svn_pool_clear(sbb->scratch_pool);
104
105  /* ### do something with this  */
106  svn_error_clear(err);
107
108  /* The spillbuf may have returned more than requested. Stash any extra
109     into our holding area.  */
110  if (requested < *len)
111    {
112      sbb->holding = *data + requested;
113      sbb->hold_len = *len - requested;
114      *len = requested;
115    }
116
117  return *data == NULL ? APR_EOF : APR_SUCCESS;
118}
119
120
121static apr_status_t
122sb_bucket_readline(serf_bucket_t *bucket, int acceptable,
123                   int *found,
124                   const char **data, apr_size_t *len)
125{
126  /* ### for now, we know callers won't use this function.  */
127  svn_error_clear(svn_error__malfunction(TRUE, __FILE__, __LINE__,
128                                         "Not implemented."));
129  return APR_ENOTIMPL;
130}
131
132
133static apr_status_t
134sb_bucket_peek(serf_bucket_t *bucket,
135               const char **data, apr_size_t *len)
136{
137  struct sbb_baton *sbb = bucket->data;
138  svn_error_t *err;
139
140  /* If we're not holding any data, then fill it.  */
141  if (sbb->holding == NULL)
142    {
143      err = svn_spillbuf__read(&sbb->holding, &sbb->hold_len, sbb->spillbuf,
144                               sbb->scratch_pool);
145      svn_pool_clear(sbb->scratch_pool);
146
147      /* ### do something with this  */
148      svn_error_clear(err);
149    }
150
151  /* Return the data we are (now) holding.  */
152  *data = sbb->holding;
153  *len = sbb->hold_len;
154
155  return *data == NULL ? APR_EOF : APR_SUCCESS;
156}
157
158
159static const serf_bucket_type_t sb_bucket_vtable = {
160    "SPILLBUF",
161    sb_bucket_read,
162    sb_bucket_readline,
163    serf_default_read_iovec,
164    serf_default_read_for_sendfile,
165    serf_default_read_bucket,
166    sb_bucket_peek,
167    serf_default_destroy_and_data,
168};
169
170
171serf_bucket_t *
172svn_ra_serf__create_sb_bucket(svn_spillbuf_t *spillbuf,
173                              serf_bucket_alloc_t *allocator,
174                              apr_pool_t *result_pool,
175                              apr_pool_t *scratch_pool)
176{
177  struct sbb_baton *sbb;
178
179  sbb = serf_bucket_mem_alloc(allocator, sizeof(*sbb));
180  sbb->spillbuf = spillbuf;
181  sbb->holding = NULL;
182  sbb->scratch_pool = svn_pool_create(result_pool);
183
184  return serf_bucket_create(&sb_bucket_vtable, allocator, sbb);
185}
186