protected override Task <IActionResult> CreateItemGetResponse(FileSystemInfoBase info, string localFilePath) { // CORE TODO Similar to VfsController: File() apparently has built in support for range requests and etags. From a cursory glance // that renders bascially all of this obsolete. Will need checking to ensure proper behavior. Can we use PhysicalFile instead? var stream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); return(Task.FromResult( (IActionResult)File(stream, MediaTypeMap.GetMediaType(info.Extension).ToString(), info.LastWriteTime, _currentEtag))); //// Check whether we have a conditional If-None-Match request //if (IsIfNoneMatchRequest(_currentEtag)) //{ // var result = StatusCode(StatusCodes.Status304NotModified); // // CORE TODO make sure this works properly // Response.GetTypedHeaders().ETag = _currentEtag; // return Task.FromResult((IActionResult)result); //} //// Check whether we have a conditional range request containing both a Range and If-Range header field //bool isRangeRequest = IsRangeRequest(_currentEtag); //// Generate file response //try //{ // _readStream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); // MediaTypeHeaderValue mediaType = MediaTypeMap.GetMediaType(info.Extension); // HttpResponseMessage successFileResponse = Request.CreateResponse(isRangeRequest ? HttpStatusCode.PartialContent : HttpStatusCode.OK); // if (isRangeRequest) // { // successFileResponse.Content = new ByteRangeStreamContent(_readStream, Request.Headers.Range, mediaType, BufferSize); // } // else // { // successFileResponse.Content = new StreamContent(_readStream, BufferSize); // successFileResponse.Content.Headers.ContentType = mediaType; // } // // Set etag for the file // successFileResponse.Headers.ETag = _currentEtag; // return Task.FromResult(successFileResponse); //} //catch (InvalidByteRangeException invalidByteRangeException) //{ // // The range request had no overlap with the current extend of the resource so generate a 416 (Requested Range Not Satisfiable) // // including a Content-Range header with the current size. // Tracer.TraceError(invalidByteRangeException); // HttpResponseMessage invalidByteRangeResponse = Request.CreateErrorResponse(invalidByteRangeException); // CloseReadStream(); // return Task.FromResult(invalidByteRangeResponse); //} //catch (Exception ex) //{ // // Could not read the file // Tracer.TraceError(ex); // HttpResponseMessage errorResponse = Request.CreateErrorResponse(HttpStatusCode.NotFound, ex); // CloseReadStream(); // return Task.FromResult(errorResponse); //} }
protected override Task <HttpResponseMessage> CreateItemGetResponse(FileSystemInfoBase info, string localFilePath) { // Check whether we have a conditional If-None-Match request if (IsIfNoneMatchRequest(_currentEtag)) { HttpResponseMessage notModifiedResponse = Request.CreateResponse(HttpStatusCode.NotModified); notModifiedResponse.Headers.ETag = _currentEtag; return(Task.FromResult(notModifiedResponse)); } // Check whether we have a conditional range request containing both a Range and If-Range header field bool isRangeRequest = IsRangeRequest(_currentEtag); // Generate file response try { _readStream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); MediaTypeHeaderValue mediaType = MediaTypeMap.GetMediaType(info.Extension); HttpResponseMessage successFileResponse = Request.CreateResponse(isRangeRequest ? HttpStatusCode.PartialContent : HttpStatusCode.OK); if (isRangeRequest) { successFileResponse.Content = new ByteRangeStreamContent(_readStream, Request.Headers.Range, mediaType, BufferSize); } else { successFileResponse.Content = new StreamContent(_readStream, BufferSize); successFileResponse.Content.Headers.ContentType = mediaType; } // Set etag for the file successFileResponse.Headers.ETag = _currentEtag; return(Task.FromResult(successFileResponse)); } catch (InvalidByteRangeException invalidByteRangeException) { // The range request had no overlap with the current extend of the resource so generate a 416 (Requested Range Not Satisfiable) // including a Content-Range header with the current size. Tracer.TraceError(invalidByteRangeException); HttpResponseMessage invalidByteRangeResponse = Request.CreateErrorResponse(invalidByteRangeException); CloseReadStream(); return(Task.FromResult(invalidByteRangeResponse)); } catch (Exception ex) { // Could not read the file Tracer.TraceError(ex); HttpResponseMessage errorResponse = Request.CreateErrorResponse(HttpStatusCode.NotFound, ex); CloseReadStream(); return(Task.FromResult(errorResponse)); } }
protected override async Task<HttpResponseMessage> CreateItemPutResponse(FileSystemInfoBase info, string localFilePath, bool itemExists) { // If repository is empty then there is no commit id and no master branch so we don't create any branch; we just init the repo. if (_currentEtag != null) { HttpResponseMessage errorResponse; if (!PrepareBranch(itemExists, out errorResponse)) { return errorResponse; } } else { // Initialize or re-initialize repository _repository.Initialize(); } // Save file try { // Get the query parameters QueryParameters parameters = new QueryParameters(this.Request); using (Stream fileStream = GetFileWriteStream(localFilePath, fileExists: itemExists)) { try { await Request.Content.CopyToAsync(fileStream); } catch (Exception ex) { Tracer.TraceError(ex); HttpResponseMessage conflictResponse = Request.CreateErrorResponse( HttpStatusCode.Conflict, RS.Format(Resources.VfsController_WriteConflict, localFilePath, ex.Message), ex); return conflictResponse; } } // Use to track whether our rebase applied updates from master. bool updateBranchIsUpToDate = true; // Commit to local branch bool commitResult = _repository.Commit(parameters.Message, authorName: null, emailAddress: null); if (!commitResult) { HttpResponseMessage noChangeResponse = Request.CreateResponse(HttpStatusCode.NoContent); noChangeResponse.Headers.ETag = CreateEtag(_repository.CurrentId); return noChangeResponse; } bool rebasing = false; if (_currentEtag != null) { try { // Only rebase if VFS branch isn't up-to-date already if (!_repository.DoesBranchContainCommit(VfsUpdateBranch, MasterBranch)) { // Rebase to get updates from master while checking whether we get a conflict rebasing = true; updateBranchIsUpToDate = _repository.Rebase(MasterBranch); } // Switch content back to master _repository.UpdateRef(VfsUpdateBranch); } catch (CommandLineException commandLineException) { Tracer.TraceError(commandLineException); if (rebasing) { // The rebase resulted in a conflict. We send the conflicted version to the client so that the user // can see the conflicts and resubmit. _cleanupRebaseConflict = true; HttpResponseMessage conflictResponse = Request.CreateResponse(HttpStatusCode.Conflict); _readStream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); conflictResponse.Content = new StreamContent(_readStream, BufferSize); conflictResponse.Content.Headers.ContentType = _conflictMediaType; return conflictResponse; } else { HttpResponseMessage updateErrorResponse = Request.CreateErrorResponse(HttpStatusCode.InternalServerError, RS.Format(Resources.VfsScmUpdate_Error, commandLineException.Message)); return updateErrorResponse; } } } // If item does not already exist then we return 201 Created. Otherwise, as a successful commit could result // in a non-conflicting merge we send back the committed version so that a client // can get the latest bits. This means we use a 200 OK response instead of a 204 response. HttpResponseMessage successFileResponse = null; if (itemExists) { if (updateBranchIsUpToDate) { successFileResponse = Request.CreateResponse(HttpStatusCode.NoContent); } else { successFileResponse = Request.CreateResponse(HttpStatusCode.OK); _readStream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); successFileResponse.Content = new StreamContent(_readStream, BufferSize); successFileResponse.Content.Headers.ContentType = MediaTypeMap.GetMediaType(info.Extension); } } else { successFileResponse = Request.CreateResponse(HttpStatusCode.Created); } // Get current commit ID string currentId = _repository.CurrentId; // Deploy changes unless request indicated to not deploy if (!parameters.NoDeploy) { DeployResult result = await DeployChangesAsync(currentId); if (result != null && result.Status != DeployStatus.Success) { HttpResponseMessage deploymentErrorResponse = Request.CreateErrorResponse(HttpStatusCode.InternalServerError, RS.Format(Resources.VfsScmController_DeploymentError, result.StatusText)); return deploymentErrorResponse; } } // Set updated etag for the file successFileResponse.Headers.ETag = CreateEtag(currentId); return successFileResponse; } catch (Exception ex) { Tracer.TraceError(ex); HttpResponseMessage errorResponse = Request.CreateErrorResponse(HttpStatusCode.Conflict, RS.Format(Resources.VfsController_WriteConflict, localFilePath, ex.Message), ex); return errorResponse; } }
protected override Task<HttpResponseMessage> CreateItemGetResponse(FileSystemInfoBase info, string localFilePath) { // Check whether we have a conditional If-None-Match request if (IsIfNoneMatchRequest(_currentEtag)) { HttpResponseMessage notModifiedResponse = Request.CreateResponse(HttpStatusCode.NotModified); notModifiedResponse.Headers.ETag = _currentEtag; return Task.FromResult(notModifiedResponse); } // Check whether we have a conditional range request containing both a Range and If-Range header field bool isRangeRequest = IsRangeRequest(_currentEtag); // Generate file response try { _readStream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); MediaTypeHeaderValue mediaType = MediaTypeMap.GetMediaType(info.Extension); HttpResponseMessage successFileResponse = Request.CreateResponse(isRangeRequest ? HttpStatusCode.PartialContent : HttpStatusCode.OK); if (isRangeRequest) { successFileResponse.Content = new ByteRangeStreamContent(_readStream, Request.Headers.Range, mediaType, BufferSize); } else { successFileResponse.Content = new StreamContent(_readStream, BufferSize); successFileResponse.Content.Headers.ContentType = mediaType; } // Set etag for the file successFileResponse.Headers.ETag = _currentEtag; return Task.FromResult(successFileResponse); } catch (InvalidByteRangeException invalidByteRangeException) { // The range request had no overlap with the current extend of the resource so generate a 416 (Requested Range Not Satisfiable) // including a Content-Range header with the current size. Tracer.TraceError(invalidByteRangeException); HttpResponseMessage invalidByteRangeResponse = Request.CreateErrorResponse(invalidByteRangeException); CloseReadStream(); return Task.FromResult(invalidByteRangeResponse); } catch (Exception ex) { // Could not read the file Tracer.TraceError(ex); HttpResponseMessage errorResponse = Request.CreateErrorResponse(HttpStatusCode.NotFound, ex); CloseReadStream(); return Task.FromResult(errorResponse); } }
protected override async Task <IActionResult> CreateItemPutResponse(FileSystemInfoBase info, string localFilePath, bool itemExists) { // If repository is empty then there is no commit id and no master branch so we don't create any branch; we just init the repo. if (_currentEtag != null) { if (!PrepareBranch(itemExists, out IActionResult errorResponse)) { return(errorResponse); } } else { // Initialize or re-initialize repository _repository.Initialize(); } // Save file try { // Get the query parameters QueryParameters parameters = new QueryParameters(this.Request); using (Stream fileStream = GetFileWriteStream(localFilePath, fileExists: itemExists)) { try { await Request.Body.CopyToAsync(fileStream); } catch (Exception ex) { Tracer.TraceError(ex); return(StatusCode(StatusCodes.Status409Conflict, RS.Format(Resources.VfsController_WriteConflict, localFilePath, ex.Message))); } } // Use to track whether our rebase applied updates from master. bool updateBranchIsUpToDate = true; // Commit to local branch bool commitResult = _repository.Commit(parameters.Message, authorName: null, emailAddress: null); if (!commitResult) { // TODO this is janky here, we return an actionresult but set the etag on the response directly Response.GetTypedHeaders().ETag = CreateEtag(_repository.CurrentId); return(NoContent()); } bool rebasing = false; if (_currentEtag != null) { try { // Only rebase if VFS branch isn't up-to-date already if (!_repository.DoesBranchContainCommit(VfsUpdateBranch, MasterBranch)) { // Rebase to get updates from master while checking whether we get a conflict rebasing = true; updateBranchIsUpToDate = _repository.Rebase(MasterBranch); } // Switch content back to master _repository.UpdateRef(VfsUpdateBranch); } catch (CommandLineException commandLineException) { Tracer.TraceError(commandLineException); if (rebasing) { // The rebase resulted in a conflict. We send the conflicted version to the client so that the user // can see the conflicts and resubmit. _cleanupRebaseConflict = true; // CORE TODO not sure this works; I'm not sure you can set StatusCode for a File result Response.StatusCode = StatusCodes.Status409Conflict; _readStream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); // CORE TODO make sure mediatype.tostring works as intended here return(File(_readStream, _conflictMediaType.ToString())); //HttpResponseMessage conflictResponse = Request.CreateResponse(HttpStatusCode.Conflict); //_readStream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); //conflictResponse.Content = new StreamContent(_readStream, BufferSize); //conflictResponse.Content.Headers.ContentType = _conflictMediaType; //return conflictResponse; } else { return(StatusCode(StatusCodes.Status500InternalServerError, RS.Format(Resources.VfsScmUpdate_Error, commandLineException.Message))); } } } // If item does not already exist then we return 201 Created. Otherwise, as a successful commit could result // in a non-conflicting merge we send back the committed version so that a client // can get the latest bits. This means we use a 200 OK response instead of a 204 response. IActionResult successFileResponse = null; if (itemExists) { if (updateBranchIsUpToDate) { successFileResponse = NoContent(); } else { _readStream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); successFileResponse = File(_readStream, MediaTypeMap.GetMediaType(info.Extension).ToString()); } } else { successFileResponse = StatusCode(StatusCodes.Status201Created); } // Get current commit ID string currentId = _repository.CurrentId; // Deploy changes unless request indicated to not deploy if (!parameters.NoDeploy) { DeployResult result = await DeployChangesAsync(currentId); if (result != null && result.Status != DeployStatus.Success) { return(StatusCode(StatusCodes.Status500InternalServerError, RS.Format(Resources.VfsScmController_DeploymentError, result.StatusText))); } } // Set updated etag for the file Response.GetTypedHeaders().ETag = CreateEtag(currentId); return(successFileResponse); } catch (Exception ex) { Tracer.TraceError(ex); return(StatusCode(StatusCodes.Status409Conflict, RS.Format(Resources.VfsController_WriteConflict, localFilePath, ex.Message))); } }
private void CloseReadStream() { if (_readStream != null) { _readStream.Close(); _readStream = null; } }
protected override Task<HttpResponseMessage> CreateItemPutResponse(FileSystemInfo info, string localFilePath, bool itemExists) { // If repository is empty then there is no commit id and no master branch so we don't create any branch; we just init the repo. if (_currentEtag != null) { HttpResponseMessage errorResponse; if (!PrepareBranch(itemExists, out errorResponse)) { return Task.FromResult(errorResponse); } } else { // Initialize or re-initialize repository _repository.Initialize(); } // Save file Stream fileStream = null; try { fileStream = GetFileWriteStream(localFilePath, fileExists: itemExists); return Request.Content.CopyToAsync(fileStream) .Then(() => { // Successfully saved the file fileStream.Close(); fileStream = null; // Use to track whether our rebase applied updates from master. bool updateBranchIsUpToDate = false; // Commit to local branch ChangeSet commitResult = _repository.Commit(String.Format("Committing update from request {0}", Request.RequestUri), authorName: null); if (commitResult == null) { HttpResponseMessage noChangeResponse = Request.CreateResponse(HttpStatusCode.NoContent); noChangeResponse.Headers.ETag = CreateEtag(_repository.CurrentId); return noChangeResponse; } if (_currentEtag != null) { try { // Rebase to get updates from master while checking whether we get a conflict updateBranchIsUpToDate = _repository.Rebase(MasterBranch); // Switch content back to master _repository.UpdateRef(VfsUpdateBranch); } catch (CommandLineException commandLineException) { Tracer.TraceError(commandLineException); // The rebase resulted in a conflict. We send the conflicted version to the client so that the user // can see the conflicts and resubmit. _cleanupRebaseConflict = true; HttpResponseMessage conflictResponse = Request.CreateResponse(HttpStatusCode.Conflict); _readStream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); conflictResponse.Content = new StreamContent(_readStream, BufferSize); conflictResponse.Content.Headers.ContentType = _conflictMediaType; return conflictResponse; } } // If item does not already exist then we return 201 Created. Otherwise, as a successful commit could result // in a non-conflicting merge we send back the committed version so that a client // can get the latest bits. This means we use a 200 OK response instead of a 204 response. HttpResponseMessage successFileResponse = null; if (itemExists) { if (updateBranchIsUpToDate) { successFileResponse = Request.CreateResponse(HttpStatusCode.NoContent); } else { successFileResponse = Request.CreateResponse(HttpStatusCode.OK); _readStream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); successFileResponse.Content = new StreamContent(_readStream, BufferSize); successFileResponse.Content.Headers.ContentType = MediaTypeMap.GetMediaType(info.Extension); } } else { successFileResponse = Request.CreateResponse(HttpStatusCode.Created); } // Set updated etag for the file successFileResponse.Headers.ETag = CreateEtag(_repository.CurrentId); return successFileResponse; }, runSynchronously: true) .Catch((catchInfo) => { Tracer.TraceError(catchInfo.Exception); HttpResponseMessage conflictResponse = Request.CreateErrorResponse( HttpStatusCode.Conflict, RS.Format(Resources.VfsController_WriteConflict, localFilePath), catchInfo.Exception); if (fileStream != null) { fileStream.Close(); } return catchInfo.Handled(conflictResponse); }); } catch (Exception e) { Tracer.TraceError(e); HttpResponseMessage errorResponse = Request.CreateErrorResponse(HttpStatusCode.Conflict, RS.Format(Resources.VfsController_WriteConflict, localFilePath), e); if (fileStream != null) { fileStream.Close(); } return Task.FromResult(errorResponse); } }
protected override async Task <HttpResponseMessage> CreateItemPutResponse(FileSystemInfo info, string localFilePath, bool itemExists) { // If repository is empty then there is no commit id and no master branch so we don't create any branch; we just init the repo. if (_currentEtag != null) { HttpResponseMessage errorResponse; if (!PrepareBranch(itemExists, out errorResponse)) { return(errorResponse); } } else { // Initialize or re-initialize repository _repository.Initialize(); } // Save file try { using (Stream fileStream = GetFileWriteStream(localFilePath, fileExists: itemExists)) { try { await Request.Content.CopyToAsync(fileStream); } catch (Exception ex) { Tracer.TraceError(ex); HttpResponseMessage conflictResponse = Request.CreateErrorResponse( HttpStatusCode.Conflict, RS.Format(Resources.VfsController_WriteConflict, localFilePath), ex); return(conflictResponse); } } // Use to track whether our rebase applied updates from master. bool updateBranchIsUpToDate = false; // Commit to local branch bool commitResult = _repository.Commit(String.Format("Committing update from request {0}", Request.RequestUri), authorName: null); if (!commitResult) { HttpResponseMessage noChangeResponse = Request.CreateResponse(HttpStatusCode.NoContent); noChangeResponse.Headers.ETag = CreateEtag(_repository.CurrentId); return(noChangeResponse); } if (_currentEtag != null) { try { // Rebase to get updates from master while checking whether we get a conflict updateBranchIsUpToDate = _repository.Rebase(MasterBranch); // Switch content back to master _repository.UpdateRef(VfsUpdateBranch); } catch (CommandLineException commandLineException) { Tracer.TraceError(commandLineException); // The rebase resulted in a conflict. We send the conflicted version to the client so that the user // can see the conflicts and resubmit. _cleanupRebaseConflict = true; HttpResponseMessage conflictResponse = Request.CreateResponse(HttpStatusCode.Conflict); _readStream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); conflictResponse.Content = new StreamContent(_readStream, BufferSize); conflictResponse.Content.Headers.ContentType = _conflictMediaType; return(conflictResponse); } } // If item does not already exist then we return 201 Created. Otherwise, as a successful commit could result // in a non-conflicting merge we send back the committed version so that a client // can get the latest bits. This means we use a 200 OK response instead of a 204 response. HttpResponseMessage successFileResponse = null; if (itemExists) { if (updateBranchIsUpToDate) { successFileResponse = Request.CreateResponse(HttpStatusCode.NoContent); } else { successFileResponse = Request.CreateResponse(HttpStatusCode.OK); _readStream = new RepositoryItemStream(this, GetFileReadStream(localFilePath)); successFileResponse.Content = new StreamContent(_readStream, BufferSize); successFileResponse.Content.Headers.ContentType = MediaTypeMap.GetMediaType(info.Extension); } } else { successFileResponse = Request.CreateResponse(HttpStatusCode.Created); } // Deploy changes DeployResult result = DeployChanges(); if (result != null && result.Status != DeployStatus.Success) { HttpResponseMessage deploymentErrorResponse = Request.CreateErrorResponse(HttpStatusCode.InternalServerError, RS.Format(Resources.VfsScmController_DeploymentError, result.StatusText)); return(deploymentErrorResponse); } // Set updated etag for the file successFileResponse.Headers.ETag = CreateEtag(_repository.CurrentId); return(successFileResponse); } catch (Exception e) { Tracer.TraceError(e); HttpResponseMessage errorResponse = Request.CreateErrorResponse(HttpStatusCode.Conflict, RS.Format(Resources.VfsController_WriteConflict, localFilePath), e); return(errorResponse); } }