2

I have an Angular5 user interface with a file upload function. The user clicks a button and selects a file and the file is sent to the web api (asp.NET Core) method for processing.

This works fine with smaller files, but with larger files the request times out with a 502 error.

I can see the request always timesout at 120 seconds. (NOTE: I am hosting via node in development and via IIS in production).

In the case of large files I need to extend this timeout to a larger value. I've tried to achieve this in a number of ways:

  1. Request Header - Timeout of request in angular code. I used the following code to try to set the timeout header value but it doesn't effect the 120 seconds:

    export class AuthTokenInterceptor implements HttpInterceptor {
    
    constructor(private authContext: AuthContext) {
    }
    
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const authHeaderValue = this.authContext.getAuthenticationHeaderValue(req.url);
    
        if (authHeaderValue) {
            const authReq = req.clone({  headers:
              req.headers.set('Authorization', authHeaderValue)
                         .set('Timeout', '100000000000000000') });
    
            return next.handle(authReq);
        }
        return next.handle(req);
    }    }
    
  2. web.config - I've tried setting the httpRuntime timeout value in the web.config file to the following (but still times out at 120 seconds):

  3. Within IIS - I've tried setting the configuration of the "Limits" property in IIS and again, still times out at 120 seconds (and this has no relevance when I'm running through node server).

enter image description here

Has anyone been able to modify this 120 seconds in their Angular(2+) app requests?

Thanks for any pointers in advance!

NOTE: just for completeness, here's my asp.net core, controller method for uploading:

[HttpPost("Upload")]
public async Task<IActionResult> UploadAsync(IFormFile file)
{
    // Ensure the file has contents before processing.
    if (file == null || file.Length == 0)
        throw new ApiException("Csv file should not be null", HttpStatusCode.BadRequest)
            .AddApiExceptionResponseDetails(ErrorTypeCode.ValidationError, ErrorCode.BelowMinimumLength, SOURCE); 

    // Ensure the file is not over the allowed limit.
    if (file.Length > (_settings.MaxCsvFileSize * 1024))
        throw new ApiException("Max file size exceeded, limit of " + _settings.MaxCsvFileSize + "mb", HttpStatusCode.BadRequest)
            .AddApiExceptionResponseDetails(ErrorTypeCode.ValidationError, ErrorCode.ExceedsMaximumLength, SOURCE); 

    // Ensure the file type is csv and content type is correct for the file.
    if (Path.GetExtension(file.FileName) != ".csv" || 
        !Constants.CsvAcceptedContentType.Contains(file.ContentType.ToLower(CultureInfo.InvariantCulture)))
            throw new ApiException("Csv content only accepted").AddApiExceptionResponseDetails(ErrorTypeCode.ValidationError, ErrorCode.Invalid, SOURCE);

    // Read csv content.
    var content = await file.ReadCsvAsync<OrderCsvResponseDto>() as CsvProcessedResponseDto<OrderCsvResponseDto>;

    await ProcessBulkUpload(content);

    // Return information about the csv file.
    return Ok(content);
}

Note - when I run the web api via IIS Express then it times out, I've run it using the command host and it doesn't time out - seem's like this may be related to an IIS setting of sorts. The web api doesn't have a web.config file due to the new version of ASP.net Core I'm using but this piece of code doesn't seem to have any bearing on IIS Express when I run through it:

      var host = new WebHostBuilder()
                .UseStartup<Startup>()
                .UseKestrel(o => {

                    o.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(10);
                    o.ShutdownTimeout = TimeSpan.FromMinutes(10);
                    o.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(10);

                })
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseApplicationInsights()
                .Build();
Rob
  • 6,819
  • 17
  • 71
  • 131
  • Have you seen this - https://stackoverflow.com/a/38507709/5740031 ? – CodeFuller Mar 12 '18 at 05:54
  • Thanks but that didn't help - I tried this on the API side as the client side is Angular but still no joy – Rob Mar 12 '18 at 09:33
  • I was able to reproduce 502 error if request executes longer than 120 seconds. I also was able to fix it by adding `requestTimeout="00:20:00"` to `aspNetCore` section in web.config as [this answer](https://stackoverflow.com/a/38507709/5740031) suggests. Please recheck. – CodeFuller Mar 12 '18 at 10:55
  • You didn't need to change anything on the UI perspective? So just add this to the Web Api web.config? – Rob Mar 12 '18 at 14:14
  • I have tried it without any specific client code. I believe the problem is on the server side. You could verify it by adding some simple GET method with long `Sleep()` and calling it with browser, without Angular layer. – CodeFuller Mar 12 '18 at 14:59
  • It does seem to be web api config rather than something UI specific (tested the upload via UI and via Postman and both yield the same results). Interestingly, when I run via command host instead of IIS Express, the timeout doesn't happen. So it seems to be some setting in IIS that's causing the problem. I've tried a coded solution but no luck! – Rob Mar 13 '18 at 11:04

1 Answers1

3

I will post this here in case anyone else comes across the same problem I was having. It turns out that running in IIS Express (or hosted IIS) that the config setting overrides whatever you have in code (maybe this is because the newer version of .net Core doesn't have a web.config file in the project - I'm not sure).

Anyway, I worked around this problem by carrying out the following steps:

Open IIS Express in your taskbar

enter image description here

Click on the app you are running (and wish to extend the request timeout for). Clicking the app shows the config file of the application. Double click to open the config file.

enter image description here

Apply the following setting to the aspNetCore section:

requestTimeout="00:20:00"

Example:

   <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" forwardWindowsAuthToken="false" requestTimeout="00:20:00" stdoutLogEnabled="false" />
      <httpCompression>
        <dynamicCompression>
          <add mimeType="text/event-stream" enabled="false" />
        </dynamicCompression>
      </httpCompression>
    </system.webServer>

And that's it!

NOTE: I am hosting the app in PaaS ASE - so cant configure IIS directly here. My solution for this was now to add a web.config file to the api project, and apply my setting within it. The build process honours the web.config you've got already instead of generating one on the fly and it will keep the changes needed. In my case, the web.config looked like this:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" forwardWindowsAuthToken="false" requestTimeout="00:20:00" stdoutLogEnabled="false" />
  </system.webServer>
</configuration>

Hopefully this helps others!

Rob
  • 6,819
  • 17
  • 71
  • 131