diff options
Diffstat (limited to 'subversion/libsvn_ra_serf')
-rw-r--r-- | subversion/libsvn_ra_serf/commit.c | 25 | ||||
-rw-r--r-- | subversion/libsvn_ra_serf/ra_serf.h | 12 | ||||
-rw-r--r-- | subversion/libsvn_ra_serf/replay.c | 37 | ||||
-rw-r--r-- | subversion/libsvn_ra_serf/serf.c | 29 | ||||
-rw-r--r-- | subversion/libsvn_ra_serf/util.c | 261 |
5 files changed, 244 insertions, 120 deletions
diff --git a/subversion/libsvn_ra_serf/commit.c b/subversion/libsvn_ra_serf/commit.c index 56a2bce..9682a65 100644 --- a/subversion/libsvn_ra_serf/commit.c +++ b/subversion/libsvn_ra_serf/commit.c @@ -397,10 +397,18 @@ checkout_dir(dir_context_t *dir, { if (p_dir->added) { + /* Calculate the working_url by skipping the shared ancestor bewteen + * the parent->relpath and dir->relpath. This is safe since an + * add is guaranteed to have a parent that is checked out. */ + dir_context_t *parent = p_dir->parent_dir; + const char *relpath = svn_relpath_skip_ancestor(parent->relpath, + dir->relpath); + /* Implicitly checkout this dir now. */ + SVN_ERR_ASSERT(parent->working_url); dir->working_url = svn_path_url_add_component2( - dir->parent_dir->working_url, - dir->name, dir->pool); + parent->working_url, + relpath, dir->pool); return SVN_NO_ERROR; } p_dir = p_dir->parent_dir; @@ -1924,7 +1932,18 @@ add_file(const char *path, if (handler->sline.code != 404) { - return svn_error_createf(SVN_ERR_RA_DAV_ALREADY_EXISTS, NULL, + if (handler->sline.code != 200) + { + svn_error_t *err; + + err = svn_ra_serf__error_on_status(handler->sline, + handler->path, + handler->location); + + SVN_ERR(err); + } + + return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, _("File '%s' already exists"), path); } } diff --git a/subversion/libsvn_ra_serf/ra_serf.h b/subversion/libsvn_ra_serf/ra_serf.h index f6310b3..335a9e3 100644 --- a/subversion/libsvn_ra_serf/ra_serf.h +++ b/subversion/libsvn_ra_serf/ra_serf.h @@ -57,13 +57,6 @@ extern "C" { /** Use this to silence compiler warnings about unused parameters. */ #define UNUSED_CTX(x) ((void)(x)) -/** Our User-Agent string. */ -#define USER_AGENT "SVN/" SVN_VER_NUMBER " (" SVN_BUILD_TARGET ")" \ - " serf/" \ - APR_STRINGIFY(SERF_MAJOR_VERSION) "." \ - APR_STRINGIFY(SERF_MINOR_VERSION) "." \ - APR_STRINGIFY(SERF_PATCH_VERSION) - /** Wait duration (in microseconds) used in calls to serf_context_run() */ #define SVN_RA_SERF__CONTEXT_RUN_DURATION 500000 @@ -658,11 +651,6 @@ struct svn_ra_serf__xml_parser_t { See libsvn_ra_serf/util.c */ struct svn_ra_serf__pending_t *pending; - - /* Response restart support */ - const void *headers_baton; /* Last pointer to headers */ - apr_off_t skip_size; /* Number of bytes to skip */ - apr_off_t read_size; /* Number of bytes read from response */ }; diff --git a/subversion/libsvn_ra_serf/replay.c b/subversion/libsvn_ra_serf/replay.c index 2d385f3..66e2f58 100644 --- a/subversion/libsvn_ra_serf/replay.c +++ b/subversion/libsvn_ra_serf/replay.c @@ -90,8 +90,6 @@ typedef struct prop_info_t { typedef struct replay_context_t { apr_pool_t *src_rev_pool; apr_pool_t *dst_rev_pool; - /*file_pool is cleared after completion of each file. */ - apr_pool_t *file_pool; /* Are we done fetching this file? */ svn_boolean_t done; @@ -147,10 +145,11 @@ push_state(svn_ra_serf__xml_parser_t *parser, state == OPEN_FILE || state == ADD_FILE) { replay_info_t *info; + apr_pool_t *pool = svn_pool_create(replay_ctx->dst_rev_pool); - info = apr_palloc(replay_ctx->dst_rev_pool, sizeof(*info)); + info = apr_palloc(pool, sizeof(*info)); - info->pool = replay_ctx->dst_rev_pool; + info->pool = pool; info->parent = parser->state->private; info->baton = NULL; info->stream = NULL; @@ -160,12 +159,13 @@ push_state(svn_ra_serf__xml_parser_t *parser, else if (state == CHANGE_PROP) { prop_info_t *info; + apr_pool_t *pool = svn_pool_create(replay_ctx->dst_rev_pool); - info = apr_pcalloc(replay_ctx->dst_rev_pool, sizeof(*info)); + info = apr_pcalloc(pool, sizeof(*info)); - info->pool = replay_ctx->dst_rev_pool; + info->pool = pool; info->parent = parser->state->private; - info->prop_value = svn_stringbuf_create_empty(info->pool); + info->prop_value = svn_stringbuf_create_empty(pool); parser->state->private = info; } @@ -194,7 +194,6 @@ start_replay(svn_ra_serf__xml_parser_t *parser, /* Create a pool for the commit editor. */ ctx->dst_rev_pool = svn_pool_create(ctx->src_rev_pool); - ctx->file_pool = svn_pool_create(ctx->dst_rev_pool); SVN_ERR(svn_ra_serf__select_revprops(&ctx->props, ctx->revprop_target, @@ -334,6 +333,8 @@ start_replay(svn_ra_serf__xml_parser_t *parser, SVN_ERR(ctx->editor->close_directory(info->baton, scratch_pool)); svn_ra_serf__xml_pop_state(parser); + + svn_pool_destroy(info->pool); } else if ((state == OPEN_DIR || state == ADD_DIR) && strcmp(name.name, "open-file") == 0) @@ -341,7 +342,6 @@ start_replay(svn_ra_serf__xml_parser_t *parser, const char *file_name, *rev; replay_info_t *info; - svn_pool_clear(ctx->file_pool); file_name = svn_xml_get_attr_value("name", attrs); if (!file_name) { @@ -359,7 +359,7 @@ start_replay(svn_ra_serf__xml_parser_t *parser, SVN_ERR(ctx->editor->open_file(file_name, info->parent->baton, SVN_STR_TO_REV(rev), - ctx->file_pool, &info->baton)); + info->pool, &info->baton)); } else if ((state == OPEN_DIR || state == ADD_DIR) && strcmp(name.name, "add-file") == 0) @@ -368,7 +368,6 @@ start_replay(svn_ra_serf__xml_parser_t *parser, svn_revnum_t rev; replay_info_t *info; - svn_pool_clear(ctx->file_pool); file_name = svn_xml_get_attr_value("name", attrs); if (!file_name) { @@ -387,7 +386,7 @@ start_replay(svn_ra_serf__xml_parser_t *parser, SVN_ERR(ctx->editor->add_file(file_name, info->parent->baton, copyfrom, rev, - ctx->file_pool, &info->baton)); + info->pool, &info->baton)); } else if ((state == OPEN_FILE || state == ADD_FILE) && strcmp(name.name, "apply-textdelta") == 0) @@ -407,7 +406,7 @@ start_replay(svn_ra_serf__xml_parser_t *parser, } SVN_ERR(ctx->editor->apply_textdelta(info->baton, checksum, - ctx->file_pool, + info->pool, &textdelta, &textdelta_baton)); @@ -426,6 +425,8 @@ start_replay(svn_ra_serf__xml_parser_t *parser, SVN_ERR(ctx->editor->close_file(info->baton, checksum, scratch_pool)); svn_ra_serf__xml_pop_state(parser); + + svn_pool_destroy(info->pool); } else if (((state == OPEN_FILE || state == ADD_FILE) && strcmp(name.name, "change-file-prop") == 0) || @@ -451,14 +452,13 @@ start_replay(svn_ra_serf__xml_parser_t *parser, else info->del_prop = FALSE; + info->name = apr_pstrdup(info->pool, prop_name); if (state == OPEN_FILE || state == ADD_FILE) { - info->name = apr_pstrdup(ctx->file_pool, prop_name); info->change = ctx->editor->change_file_prop; } else { - info->name = apr_pstrdup(ctx->dst_rev_pool, prop_name); info->change = ctx->editor->change_dir_prop; } @@ -538,15 +538,14 @@ end_replay(svn_ra_serf__xml_parser_t *parser, info->prop_value = NULL; /* morph killed the stringbuf. */ #endif - if (strcmp(name.name, "change-file-prop") == 0) - prop_val = svn_base64_decode_string(morph, ctx->file_pool); - else - prop_val = svn_base64_decode_string(morph, ctx->dst_rev_pool); + prop_val = svn_base64_decode_string(morph, info->pool); } SVN_ERR(info->change(info->parent->baton, info->name, prop_val, info->parent->pool)); svn_ra_serf__xml_pop_state(parser); + + svn_pool_destroy(info->pool); } return SVN_NO_ERROR; diff --git a/subversion/libsvn_ra_serf/serf.c b/subversion/libsvn_ra_serf/serf.c index b0b6346..66f9962 100644 --- a/subversion/libsvn_ra_serf/serf.c +++ b/subversion/libsvn_ra_serf/serf.c @@ -61,11 +61,18 @@ ra_serf_version(void) #define RA_SERF_DESCRIPTION \ N_("Module for accessing a repository via WebDAV protocol using serf.") +#define RA_SERF_DESCRIPTION_VER \ + N_("Module for accessing a repository via WebDAV protocol using serf.\n" \ + " - using serf %d.%d.%d") + /* Implements svn_ra__vtable_t.get_description(). */ static const char * -ra_serf_get_description(void) +ra_serf_get_description(apr_pool_t *pool) { - return _(RA_SERF_DESCRIPTION); + int major, minor, patch; + + serf_lib_version(&major, &minor, &patch); + return apr_psprintf(pool, _(RA_SERF_DESCRIPTION_VER), major, minor, patch); } /* Implements svn_ra__vtable_t.get_schemes(). */ @@ -413,6 +420,18 @@ svn_ra_serf__progress(void *progress_baton, apr_off_t read, apr_off_t written) } } +/** Our User-Agent string. */ +static const char * +get_user_agent_string(apr_pool_t *pool) +{ + int major, minor, patch; + serf_lib_version(&major, &minor, &patch); + + return apr_psprintf(pool, "SVN/%s (%s) serf/%d.%d.%d", + SVN_VER_NUMBER, SVN_BUILD_TARGET, + major, minor, patch); +} + /* Implements svn_ra__vtable_t.open_session(). */ static svn_error_t * svn_ra_serf__open(svn_ra_session_t *session, @@ -495,10 +514,10 @@ svn_ra_serf__open(svn_ra_session_t *session, SVN_ERR(callbacks->get_client_string(callback_baton, &client_string, pool)); if (client_string) - serf_sess->useragent = apr_pstrcat(pool, USER_AGENT, " ", + serf_sess->useragent = apr_pstrcat(pool, get_user_agent_string(pool), " ", client_string, (char *)NULL); else - serf_sess->useragent = USER_AGENT; + serf_sess->useragent = get_user_agent_string(pool); /* go ahead and tell serf about the connection. */ status = @@ -1260,7 +1279,7 @@ svn_ra_serf__init(const svn_version_t *loader_version, int serf_minor; int serf_patch; - SVN_ERR(svn_ver_check_list(ra_serf_version(), checklist)); + SVN_ERR(svn_ver_check_list2(ra_serf_version(), checklist, svn_ver_equal)); /* Simplified version check to make sure we can safely use the VTABLE parameter. The RA loader does a more exhaustive check. */ diff --git a/subversion/libsvn_ra_serf/util.c b/subversion/libsvn_ra_serf/util.c index ce1e31a..55efca4 100644 --- a/subversion/libsvn_ra_serf/util.c +++ b/subversion/libsvn_ra_serf/util.c @@ -191,13 +191,65 @@ construct_realm(svn_ra_serf__session_t *session, static char * convert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool) { - return apr_psprintf(pool, "%s, %s, %s, %s, %s (%s)", - (char*)svn_hash_gets(org, "OU"), - (char*)svn_hash_gets(org, "O"), - (char*)svn_hash_gets(org, "L"), - (char*)svn_hash_gets(org, "ST"), - (char*)svn_hash_gets(org, "C"), - (char*)svn_hash_gets(org, "E")); + const char *org_unit = svn_hash_gets(org, "OU"); + const char *org_name = svn_hash_gets(org, "O"); + const char *locality = svn_hash_gets(org, "L"); + const char *state = svn_hash_gets(org, "ST"); + const char *country = svn_hash_gets(org, "C"); + const char *email = svn_hash_gets(org, "E"); + svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool); + + if (org_unit) + { + svn_stringbuf_appendcstr(buf, org_unit); + svn_stringbuf_appendcstr(buf, ", "); + } + + if (org_name) + { + svn_stringbuf_appendcstr(buf, org_name); + svn_stringbuf_appendcstr(buf, ", "); + } + + if (locality) + { + svn_stringbuf_appendcstr(buf, locality); + svn_stringbuf_appendcstr(buf, ", "); + } + + if (state) + { + svn_stringbuf_appendcstr(buf, state); + svn_stringbuf_appendcstr(buf, ", "); + } + + if (country) + { + svn_stringbuf_appendcstr(buf, country); + svn_stringbuf_appendcstr(buf, ", "); + } + + /* Chop ', ' if any. */ + svn_stringbuf_chop(buf, 2); + + if (email) + { + svn_stringbuf_appendcstr(buf, "("); + svn_stringbuf_appendcstr(buf, email); + svn_stringbuf_appendcstr(buf, ")"); + } + + return buf->data; +} + +static void append_reason(svn_stringbuf_t *errmsg, const char *reason, int *reasons) +{ + if (*reasons < 1) + svn_stringbuf_appendcstr(errmsg, _(": ")); + else + svn_stringbuf_appendcstr(errmsg, _(", ")); + svn_stringbuf_appendcstr(errmsg, reason); + (*reasons)++; } /* This function is called on receiving a ssl certificate of a server when @@ -258,11 +310,12 @@ ssl_server_cert(void *baton, int failures, for (i = 0; i < san->nelts; i++) { char *s = APR_ARRAY_IDX(san, i, char*); if (apr_fnmatch(s, conn->session->session_url.hostname, - APR_FNM_PERIOD) == APR_SUCCESS) { + APR_FNM_PERIOD | APR_FNM_CASE_BLIND) == APR_SUCCESS) + { found_matching_hostname = 1; cert_info.hostname = s; break; - } + } } } @@ -270,7 +323,7 @@ ssl_server_cert(void *baton, int failures, if (!found_matching_hostname && cert_info.hostname) { if (apr_fnmatch(cert_info.hostname, conn->session->session_url.hostname, - APR_FNM_PERIOD) == APR_FNM_NOMATCH) + APR_FNM_PERIOD | APR_FNM_CASE_BLIND) == APR_FNM_NOMATCH) { svn_failures |= SVN_AUTH_SSL_CNMISMATCH; } @@ -301,7 +354,35 @@ ssl_server_cert(void *baton, int failures, SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); if (!server_creds) - return svn_error_create(SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED, NULL, NULL); + { + svn_stringbuf_t *errmsg; + int reasons = 0; + + errmsg = svn_stringbuf_create( + _("Server SSL certificate verification failed"), + scratch_pool); + + + if (svn_failures & SVN_AUTH_SSL_NOTYETVALID) + append_reason(errmsg, _("certificate is not yet valid"), &reasons); + + if (svn_failures & SVN_AUTH_SSL_EXPIRED) + append_reason(errmsg, _("certificate has expired"), &reasons); + + if (svn_failures & SVN_AUTH_SSL_CNMISMATCH) + append_reason(errmsg, + _("certificate issued for a different hostname"), + &reasons); + + if (svn_failures & SVN_AUTH_SSL_UNKNOWNCA) + append_reason(errmsg, _("issuer is not trusted"), &reasons); + + if (svn_failures & SVN_AUTH_SSL_OTHER) + append_reason(errmsg, _("and other reason(s)"), &reasons); + + return svn_error_create(SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED, NULL, + errmsg->data); + } return SVN_NO_ERROR; } @@ -746,8 +827,6 @@ svn_ra_serf__context_run_wait(svn_boolean_t *done, the connection timed out. */ if (APR_STATUS_IS_TIMEUP(status)) { - svn_error_clear(err); - err = SVN_NO_ERROR; status = 0; if (sess->timeout) @@ -758,8 +837,11 @@ svn_ra_serf__context_run_wait(svn_boolean_t *done, } else { - return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL, - _("Connection timed out")); + return + svn_error_compose_create( + err, + svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL, + _("Connection timed out"))); } } } @@ -805,6 +887,23 @@ svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler, /* Wait until the response logic marks its DONE status. */ err = svn_ra_serf__context_run_wait(&handler->done, handler->session, scratch_pool); + + /* A callback invocation has been canceled. In this simple case of + context_run_one, we can keep the ra-session operational by resetting + the connection. + + If we don't do this, the next context run will notice that the connection + is still in the error state and will just return SVN_ERR_CEASE_INVOCATION + (=the last error for the connection) again */ + if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION) + { + apr_status_t status = serf_connection_reset(handler->conn->conn); + + if (status) + err = svn_error_compose_create(err, + svn_ra_serf__wrap_err(status, NULL)); + } + if (handler->server_error) { err = svn_error_compose_create(err, handler->server_error->error); @@ -1388,19 +1487,22 @@ inject_to_parser(svn_ra_serf__xml_parser_t *ctx, int xml_status; xml_status = XML_Parse(ctx->xmlp, data, (int) len, 0); - if (xml_status == XML_STATUS_ERROR && !ctx->ignore_errors) + + if (! ctx->ignore_errors) { - if (sl == NULL) - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("XML parsing failed")); + SVN_ERR(ctx->error); - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("XML parsing failed: (%d %s)"), - sl->code, sl->reason); - } + if (xml_status != XML_STATUS_OK) + { + if (sl == NULL) + return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + _("XML parsing failed")); - if (ctx->error && !ctx->ignore_errors) - return svn_error_trace(ctx->error); + return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + _("XML parsing failed: (%d %s)"), + sl->code, sl->reason); + } + } return SVN_NO_ERROR; } @@ -1481,14 +1583,26 @@ svn_ra_serf__process_pending(svn_ra_serf__xml_parser_t *parser, if (pending_empty && parser->pending->network_eof) { + int xml_status; SVN_ERR_ASSERT(parser->xmlp != NULL); - /* Tell the parser that no more content will be parsed. Ignore the - return status. We just don't care. */ - (void) XML_Parse(parser->xmlp, NULL, 0, 1); + /* Tell the parser that no more content will be parsed. */ + xml_status = XML_Parse(parser->xmlp, NULL, 0, 1); apr_pool_cleanup_run(parser->pool, &parser->xmlp, xml_parser_cleanup); parser->xmlp = NULL; + + if (! parser->ignore_errors) + { + SVN_ERR(parser->error); + + if (xml_status != XML_STATUS_OK) + { + return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + _("XML parsing failed")); + } + } + add_done_item(parser); } @@ -1588,22 +1702,6 @@ svn_ra_serf__handle_xml_parser(serf_request_t *request, return svn_error_trace(err); } - if (ctx->headers_baton == NULL) - ctx->headers_baton = serf_bucket_response_get_headers(response); - else if (ctx->headers_baton != serf_bucket_response_get_headers(response)) - { - /* We got a new response to an existing parser... - This tells us the connection has restarted and we should continue - where we stopped last time. - */ - - /* Is this a second attempt?? */ - if (!ctx->skip_size) - ctx->skip_size = ctx->read_size; - - ctx->read_size = 0; /* New request, nothing read */ - } - if (!ctx->xmlp) { ctx->xmlp = XML_ParserCreate(NULL); @@ -1623,41 +1721,11 @@ svn_ra_serf__handle_xml_parser(serf_request_t *request, apr_size_t len; status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len); - if (SERF_BUCKET_READ_ERROR(status)) { return svn_ra_serf__wrap_err(status, NULL); } - ctx->read_size += len; - - if (ctx->skip_size) - { - /* Handle restarted requests correctly: Skip what we already read */ - apr_size_t skip; - - if (ctx->skip_size >= ctx->read_size) - { - /* Eek. What did the file shrink or something? */ - if (APR_STATUS_IS_EOF(status)) - { - SVN_ERR_MALFUNCTION(); - } - - /* Skip on to the next iteration of this loop. */ - if (APR_STATUS_IS_EAGAIN(status)) - { - return svn_ra_serf__wrap_err(status, NULL); - } - continue; - } - - skip = (apr_size_t)(len - (ctx->read_size - ctx->skip_size)); - data += skip; - len -= skip; - ctx->skip_size = 0; - } - /* Note: once the callbacks invoked by inject_to_parser() sets the PAUSED flag, then it will not be cleared. write_to_pending() will only save the content. Logic outside of serf_context_run() will @@ -1703,12 +1771,25 @@ svn_ra_serf__handle_xml_parser(serf_request_t *request, in the PENDING structures, then we're completely done. */ if (!HAS_PENDING_DATA(ctx->pending)) { + int xml_status; SVN_ERR_ASSERT(ctx->xmlp != NULL); - /* Ignore the return status. We just don't care. */ - (void) XML_Parse(ctx->xmlp, NULL, 0, 1); + xml_status = XML_Parse(ctx->xmlp, NULL, 0, 1); apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup); + + if (! ctx->ignore_errors) + { + SVN_ERR(ctx->error); + + if (xml_status != XML_STATUS_OK) + { + return svn_error_create( + SVN_ERR_XML_MALFORMED, NULL, + _("The XML response contains invalid XML")); + } + } + add_done_item(ctx); } @@ -1828,12 +1909,26 @@ handle_response(serf_request_t *request, { /* Uh-oh. Our connection died. */ if (handler->response_error) - SVN_ERR(handler->response_error(request, response, 0, - handler->response_error_baton)); + { + /* Give a handler chance to prevent request requeue. */ + SVN_ERR(handler->response_error(request, response, 0, + handler->response_error_baton)); - /* Requeue another request for this handler. - ### how do we know if the handler can deal with this?! */ - svn_ra_serf__request_create(handler); + svn_ra_serf__request_create(handler); + } + /* Response error callback is not configured. Requeue another request + for this handler only if we didn't started to process body. + Return error otherwise. */ + else if (!handler->reading_body) + { + svn_ra_serf__request_create(handler); + } + else + { + return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, + _("%s request on '%s' failed"), + handler->method, handler->path); + } return SVN_NO_ERROR; } @@ -2417,6 +2512,10 @@ svn_ra_serf__error_on_status(serf_status_line sline, "server or an intermediate proxy does not accept " "chunked encoding. Try setting 'http-chunked-requests' " "to 'auto' or 'no' in your client configuration.")); + case 501: + return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, + _("The requested feature is not supported by " + "'%s'"), path); } if (sline.code >= 300) |