2 * get_file.c : entry point for update RA functions for ra_serf
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
26 #define APR_WANT_STRFUNC
27 #include <apr_version.h>
34 #include "svn_private_config.h"
36 #include "svn_pools.h"
38 #include "svn_delta.h"
40 #include "svn_props.h"
42 #include "private/svn_dep_compat.h"
43 #include "private/svn_string_private.h"
46 #include "../libsvn_ra/ra_loader.h"
52 * This structure represents a single request to GET (fetch) a file with
53 * its associated Serf session/connection.
55 typedef struct stream_ctx_t {
57 /* The handler representing this particular fetch. */
58 svn_ra_serf__handler_t *handler;
60 /* Have we read our response headers yet? */
61 svn_boolean_t read_headers;
63 svn_boolean_t using_compression;
65 /* This flag is set when our response is aborted before we reach the
66 * end and we decide to requeue this request.
68 svn_boolean_t aborted_read;
69 apr_off_t aborted_read_size;
71 /* This is the amount of data that we have read so far. */
74 /* If we're writing this file to a stream, this will be non-NULL. */
75 svn_stream_t *result_stream;
81 /** Routines called when we are fetching a file */
84 headers_fetch(serf_bucket_t *headers,
86 apr_pool_t *pool /* request pool */,
87 apr_pool_t *scratch_pool)
89 stream_ctx_t *fetch_ctx = baton;
91 if (fetch_ctx->using_compression)
93 serf_bucket_headers_setn(headers, "Accept-Encoding", "gzip");
100 cancel_fetch(serf_request_t *request,
101 serf_bucket_t *response,
105 stream_ctx_t *fetch_ctx = baton;
107 /* Uh-oh. Our connection died on us.
109 * The core ra_serf layer will requeue our request - we just need to note
110 * that we got cut off in the middle of our song.
114 /* If we already started the fetch and opened the file handle, we need
115 * to hold subsequent read() ops until we get back to where we were
116 * before the close and we can then resume the textdelta() calls.
118 if (fetch_ctx->read_headers)
120 if (!fetch_ctx->aborted_read && fetch_ctx->read_size)
122 fetch_ctx->aborted_read = TRUE;
123 fetch_ctx->aborted_read_size = fetch_ctx->read_size;
125 fetch_ctx->read_size = 0;
131 /* We have no idea what went wrong. */
132 SVN_ERR_MALFUNCTION();
136 /* Helper svn_ra_serf__get_file(). Attempts to fetch file contents
137 * using SESSION->wc_callbacks->get_wc_contents() if sha1 property is
140 * Sets *FOUND_P to TRUE if file contents was successfuly fetched.
142 * Performs all temporary allocations in POOL.
145 try_get_wc_contents(svn_boolean_t *found_p,
146 svn_ra_serf__session_t *session,
147 const char *sha1_checksum_prop,
148 svn_stream_t *dst_stream,
151 svn_checksum_t *checksum;
152 svn_stream_t *wc_stream;
155 /* No contents found by default. */
158 if (!session->wc_callbacks->get_wc_contents
159 || sha1_checksum_prop == NULL)
165 SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1,
166 sha1_checksum_prop, pool));
168 err = session->wc_callbacks->get_wc_contents(
169 session->wc_callback_baton, &wc_stream, checksum, pool);
173 svn_error_clear(err);
175 /* Ignore errors for now. */
181 SVN_ERR(svn_stream_copy3(wc_stream,
182 svn_stream_disown(dst_stream, pool),
190 /* -----------------------------------------------------------------------
191 svn_ra_get_file() specific */
193 /* Implements svn_ra_serf__response_handler_t */
195 handle_stream(serf_request_t *request,
196 serf_bucket_t *response,
200 stream_ctx_t *fetch_ctx = handler_baton;
203 if (fetch_ctx->handler->sline.code != 200)
204 return svn_error_trace(svn_ra_serf__unexpected_status(fetch_ctx->handler));
211 status = serf_bucket_read(response, 8000, &data, &len);
212 if (SERF_BUCKET_READ_ERROR(status))
214 return svn_ra_serf__wrap_err(status, NULL);
217 fetch_ctx->read_size += len;
219 if (fetch_ctx->aborted_read)
223 /* We haven't caught up to where we were before. */
224 if (fetch_ctx->read_size < fetch_ctx->aborted_read_size)
226 /* Eek. What did the file shrink or something? */
227 if (APR_STATUS_IS_EOF(status))
229 SVN_ERR_MALFUNCTION();
232 /* Skip on to the next iteration of this loop. */
233 if (APR_STATUS_IS_EAGAIN(status))
235 return svn_ra_serf__wrap_err(status, NULL);
240 /* Woo-hoo. We're back. */
241 fetch_ctx->aborted_read = FALSE;
243 /* Increment data and len by the difference. */
244 skip = len - (fetch_ctx->read_size - fetch_ctx->aborted_read_size);
246 len -= (apr_size_t)skip;
251 apr_size_t written_len;
255 SVN_ERR(svn_stream_write(fetch_ctx->result_stream, data,
261 return svn_ra_serf__wrap_err(status, NULL);
267 /* Baton for get_file_prop_cb */
268 struct file_prop_baton_t
270 apr_pool_t *result_pool;
271 svn_node_kind_t kind;
273 const char *sha1_checksum;
276 /* Implements svn_ra_serf__prop_func_t for svn_ra_serf__get_file */
278 get_file_prop_cb(void *baton,
282 const svn_string_t *value,
283 apr_pool_t *scratch_pool)
285 struct file_prop_baton_t *fb = baton;
286 const char *svn_name;
288 if (strcmp(ns, "DAV:") == 0 && strcmp(name, "resourcetype") == 0)
290 const char *val = value->data;
292 if (strcmp(val, "collection") == 0)
293 fb->kind = svn_node_dir;
295 fb->kind = svn_node_file;
299 else if (strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0
300 && strcmp(name, "sha1-checksum") == 0)
302 fb->sha1_checksum = apr_pstrdup(fb->result_pool, value->data);
308 svn_name = svn_ra_serf__svnname_from_wirename(ns, name, fb->result_pool);
311 svn_hash_sets(fb->props, svn_name,
312 svn_string_dup(value, fb->result_pool));
318 svn_ra_serf__get_file(svn_ra_session_t *ra_session,
320 svn_revnum_t revision,
321 svn_stream_t *stream,
322 svn_revnum_t *fetched_rev,
326 svn_ra_serf__session_t *session = ra_session->priv;
327 const char *fetch_url;
328 const svn_ra_serf__dav_props_t *which_props;
329 svn_ra_serf__handler_t *propfind_handler;
330 struct file_prop_baton_t fb;
332 /* Fetch properties. */
334 fetch_url = svn_path_url_add_component2(session->session_url.path, path, pool);
336 /* The simple case is if we want HEAD - then a GET on the fetch_url is fine.
338 * Otherwise, we need to get the baseline version for this particular
339 * revision and then fetch that file.
341 if (SVN_IS_VALID_REVNUM(revision) || fetched_rev)
343 SVN_ERR(svn_ra_serf__get_stable_url(&fetch_url, fetched_rev,
347 revision = SVN_INVALID_REVNUM;
349 /* REVISION is always SVN_INVALID_REVNUM */
350 SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision));
353 which_props = all_props;
354 else if (stream && session->wc_callbacks->get_wc_contents)
355 which_props = type_and_checksum_props;
357 which_props = check_path_props;
359 fb.result_pool = pool;
360 fb.props = props ? apr_hash_make(pool) : NULL;
361 fb.kind = svn_node_unknown;
362 fb.sha1_checksum = NULL;
364 SVN_ERR(svn_ra_serf__create_propfind_handler(&propfind_handler, session,
365 fetch_url, SVN_INVALID_REVNUM,
367 get_file_prop_cb, &fb,
370 SVN_ERR(svn_ra_serf__context_run_one(propfind_handler, pool));
372 /* Verify that resource type is not collection. */
373 if (fb.kind != svn_node_file)
375 return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL,
376 _("Can't get text contents of a directory"));
385 SVN_ERR(try_get_wc_contents(&found, session, fb.sha1_checksum, stream, pool));
387 /* No contents found in the WC, let's fetch from server. */
390 stream_ctx_t *stream_ctx;
391 svn_ra_serf__handler_t *handler;
393 /* Create the fetch context. */
394 stream_ctx = apr_pcalloc(pool, sizeof(*stream_ctx));
395 stream_ctx->result_stream = stream;
396 stream_ctx->using_compression = session->using_compression;
398 handler = svn_ra_serf__create_handler(session, pool);
400 handler->method = "GET";
401 handler->path = fetch_url;
403 handler->custom_accept_encoding = TRUE;
404 handler->no_dav_headers = TRUE;
406 handler->header_delegate = headers_fetch;
407 handler->header_delegate_baton = stream_ctx;
409 handler->response_handler = handle_stream;
410 handler->response_baton = stream_ctx;
412 handler->response_error = cancel_fetch;
413 handler->response_error_baton = stream_ctx;
415 stream_ctx->handler = handler;
417 SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
419 if (handler->sline.code != 200)
420 return svn_error_trace(svn_ra_serf__unexpected_status(handler));