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 
36 struct 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 
47 svn_error_t *
svn_ra_serf__copy_into_spillbuf(svn_spillbuf_t ** spillbuf,serf_bucket_t * bkt,apr_pool_t * result_pool,apr_pool_t * scratch_pool)48 svn_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 
77 static apr_status_t
sb_bucket_read(serf_bucket_t * bucket,apr_size_t requested,const char ** data,apr_size_t * len)78 sb_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 #if !SERF_VERSION_AT_LEAST(1, 4, 0)
121 static apr_status_t
sb_bucket_readline(serf_bucket_t * bucket,int acceptable,int * found,const char ** data,apr_size_t * len)122 sb_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 #endif
132 
133 
134 static apr_status_t
sb_bucket_peek(serf_bucket_t * bucket,const char ** data,apr_size_t * len)135 sb_bucket_peek(serf_bucket_t *bucket,
136                const char **data, apr_size_t *len)
137 {
138   struct sbb_baton *sbb = bucket->data;
139   svn_error_t *err;
140 
141   /* If we're not holding any data, then fill it.  */
142   if (sbb->holding == NULL)
143     {
144       err = svn_spillbuf__read(&sbb->holding, &sbb->hold_len, sbb->spillbuf,
145                                sbb->scratch_pool);
146       svn_pool_clear(sbb->scratch_pool);
147 
148       /* ### do something with this  */
149       svn_error_clear(err);
150     }
151 
152   /* Return the data we are (now) holding.  */
153   *data = sbb->holding;
154   *len = sbb->hold_len;
155 
156   return *data == NULL ? APR_EOF : APR_SUCCESS;
157 }
158 
159 
160 static const serf_bucket_type_t sb_bucket_vtable = {
161     "SPILLBUF",
162     sb_bucket_read,
163 #if SERF_VERSION_AT_LEAST(1, 4, 0)
164     serf_default_readline,
165 #else
166     sb_bucket_readline,
167 #endif
168     serf_default_read_iovec,
169     serf_default_read_for_sendfile,
170     serf_default_read_bucket,
171     sb_bucket_peek,
172     serf_default_destroy_and_data,
173 };
174 
175 
176 serf_bucket_t *
svn_ra_serf__create_sb_bucket(svn_spillbuf_t * spillbuf,serf_bucket_alloc_t * allocator,apr_pool_t * result_pool,apr_pool_t * scratch_pool)177 svn_ra_serf__create_sb_bucket(svn_spillbuf_t *spillbuf,
178                               serf_bucket_alloc_t *allocator,
179                               apr_pool_t *result_pool,
180                               apr_pool_t *scratch_pool)
181 {
182   struct sbb_baton *sbb;
183 
184   sbb = serf_bucket_mem_alloc(allocator, sizeof(*sbb));
185   sbb->spillbuf = spillbuf;
186   sbb->holding = NULL;
187   sbb->scratch_pool = svn_pool_create(result_pool);
188 
189   return serf_bucket_create(&sb_bucket_vtable, allocator, sbb);
190 }
191