IIS: 502 when posting multipart/form-data and using ARR - iis

We have a classic ASP.NET web api that is exposing a file upload endpoint. The file upload accepts a multipart/form-data request that holds the file. The format is:
POST https://dev.appname.com/api/v2/visitors/{{visitorId}}/attachments/upload
X-API-Key: {{apiKey}}
Content-Type: multipart/form-data; boundary=XXX
--XXX
Content-Disposition: form-data; name="image"; filename="filename.png"
Content-Type: image/png
{{ file bytes }}
--XXX--
We use the following rewriting:
<rule name="Rewrite to dev REST API" enabled="true" stopProcessing="true">
<match url="api/(v[0-9]+/.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" matchType="Pattern" pattern="(dev|dev-testing).appname.com" ignoreCase="true" negate="false" />
</conditions>
<action type="Rewrite" url="https://restapidev.appname.com/api/{R:1}?tenant={C:1}" appendQueryString="true" />
</rule>
So the url is rewritten from: https://dev.appname.com/api/v2/visitors/{{visitorId}}/attachments/upload to https://restapidev.appname.com/api/v2/visitors/{{visitorId}}/attachments/upload?tenant=dev
This gives us a response 502.
When hitting the https://restapidev.appname.com/api... url directly, the request is successful.
To diagnose the issue, I am writing the request body to a file:
private async Task SaveRequestBody() {
var req = HttpContext.Current.Request;
using var fileStream = File.OpenWrite($"C:\\temp\\{DateTime.Now:yyyy-MM-dd-HH-mm-ss}-UploadAttachment.txt");
await req.GetBufferedInputStream().CopyToAsync(fileStream);
}
I notice that:
When going through the dev.appname... url:
āŒ an empty file is written
When going through the restapidev.appname... url
āœ… the full request, including the upload is written to a file
When uploading a file without multipart/form-data (Content-Type: image/png, bytes directly posted in the request body)
āœ… the full request, including the upload is written to a file
Application logging seems to indicate that the application "hangs" on reading the request stream.
I have enabled failed request tracing, and noticed this:
At 12:36:37.144: GENERAL_READ_ENTITY_START
At 12:38:37.504: GENERAL_READ_ENTITY_END (2 minutes later!!)
BytesReceived="0", ErrorCode="The I/O operation has been aborted because of either a thread exit or an application request. (0x800703e3)"
FYI: There's a warning REWRITE_DISABLED_KERNEL_CACHE in the trace as well, but succesful requests have this warning as well.
I was not able to add the full log here (due to length restrictions on the post).
So our web application seems to "hang" while reading the request body:
only for an multipart/form-data upload
only when routed through ARR
Is there something that could be misconfigured?

Related

Controller class xxxController could not be found error

I was migrating my web application to another server. Everything works fine except for viewing any uploaded file. It keeps showing the same error whenever I tried to view the uploaded file.
Updated :
eland\app\webroot\upload
Not Working --> <?php echo $this->Html->link(__('<i class="icon-file"></i>'), '../'.ELAND.'/upload/'.trim($rec['Bahanrujukan']['filename']), array('class' => 'btn btn-default btn-small','target'=>'_blank', 'escape' => false)); ?>
eland\app\webroot\img\imgs
Working --> <?php echo $this->Html->link(__('<i class="icon-file"></i>'), '../'.ELAND.'/img/imgs/'.trim($rec['Bahanrujukan']['filename']), array('class' => 'btn btn-default btn-small','target'=>'_blank', 'escape' => false)); ?>
Should I check the plugin or the controller?
It turns out that, the issue was misconfiguration in web.config. During translation from Apache (.htaccess) to IIS (web.config), the 'upload' folder was not granted for access to the webroot.
<rule name="Rewrite routed access to assets(img, css, files, js, favicon)" stopProcessing="true"> <match url="^(img|css|files|js|upload|favicon.ico)(.*)$" /> <action type="Rewrite" url="webroot/{R:1}{R:2}" appendQueryString="false" /></rule>
thanks guys for helping me out!

IIS ReWrite catch .jpg?w=960&h=980

I have created IIS Outbound Rules to rewrite to CDN. Example for .mov files
<rule name="CDN-01-mov" preCondition="CheckHTML" stopProcessing="true">
<match filterByTags="Img, CustomTags" customTags="src" pattern="http(s)?://www.(example.com)(/public)(/uploads)(.*\.mov)" />
<action type="Rewrite" value="https://cdn.example.com/public/uploads{R:5}" />
</rule>
However, I would also like to catch .jpg files. The problem is that the tags with .jpg files in my application are like this:
files/storage/327/9071615039504vishbjwcgbftdy2my1p6.jpg?w=960&h=980
so it has the ?w=960&h=980 part which I don't know how to catch with regex.
How could I make a regex rule to catch .jpg[anything] ?
Thanks
Alex
The character before matching .jpg is R1, the character matching .jpg is R2, and the character string after .jpg is R3.
This is Pattern:
(.*)(.jpg)(.*)
Here is for your reference: Regular Expression Language - Quick Reference

Yii2 Framework to Azure Web App (404 Page Not Found)

Thank for reading my post.
I'm having an issue making my Website Application(Yii2) works correctly in Azure Web Apps. I've migrated my Yii2 Websites from an existing server to the Azure Web Apps. After lots of manipulation listed below, Iā€™m able to access the Website.
The main theme loads successfully but throwing "404 Page not found
The requested page is missing, or the address has been changed" Error in the body and others links in the "Menu Bar" not working too. "The resource you are looking for has been removed, had its name changed, or is temporarily unavailable."
The Azure Resources are configured as below:
1 x App Service Plan : S1:1 (Windows Base)
1 x Azure Database for MySQL server
1 x App Service (The Web App)
App Service Application Setting:
.Net Framwork: v4.7
PHP Version: 7.0
Platform: 32bit
Migration Steps:
FTP all files/folder from htdocs in my existing server to my App Service Plan (\site/wwwroot)
Generate the SQL Table creation in PhpAdmin
Find and Replace ENGINE=InnoDB DEFAULT CHARSET=utf8; in the script
Connect to Azure Database for MySQL server, create new Schema and Run the Script
From Azure, KUDU Inferface browse (\site\wwwroot\config) and change the db.php to
<?php
return [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=xxxx.mysql.database.azure.com;dbname=xxxxx',
'username' => 'xxxxxxx#xxxxxxxxxxxxxxxxxx',
'password' => 'xxxxxx',
'charset' => 'utf8',
];
Restart the Web App
Check the https://yourdomain.com/requirements.php (Passed without any Error)
"The resource you are looking for has been removed, had its name changed, or is temporarily unavailable" when trying to browse the new Web App.
KUDO PowerShell windows browse back to site/wwwroot and executed the following line of codes
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
php composer.phar global require "fxp/composer-asset-plugin:^1.2.0"
Browse to the Directory (\site\wwwroot\web) and change index.php with the following line of codes:
if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
if(!headers_sent()) {
header("Status: 301 Moved Permanently");
header(sprintf(
'Location: https://%s%s',
$_SERVER['HTTP_HOST'],
$_SERVER['REQUEST_URI']
));
exit();
}
}
// comment out the following two lines when deployed to production
('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_ENV') or define('YII_ENV', 'prod');
require(__DIR__ . '/../vendor/autoload.php');
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
$config = require(__DIR__ . '/../config/web.php');
(new yii\web\Application($config))->run();
From the Azure Portal / Web App / Application settinh, .change the default directory from site\wwwroot to site\wwwroot\web
Restart the Web App.
The home page them now loads successfully but with a 404 Error within the body and "The resource you are looking for has been removed, had its name changed, or is temporarily unavailable." when trying to access other pages in the header.
I also noted that I have 2 .htacss file
1x in my \site\wwwroot and 1x in my \site\wwwroot\web
Haved tried deleting them and configure a web.config file in (site\wwwroot\web) but still not working.
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Redirect rquests to default azure websites domain" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAny">
<add input="{HTTP_HOST}" pattern="^yoursite\.azurewebsites\.net$" />
</conditions>
<action type="Redirect" url="https://mysitename.azurewebsites.net/{R:0}" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
As Yii2 is not famous in Azure Web App, can't find a solution anywhere. Will appreciate if you can help along.

IIS 8.5 MVC5 Client Cache is ignored

TL;DR - I want the server (IIS 8.5) to return 304 not modified for the CSS and JS bundles.
I've been unable to get IIS 8.5 to honor the clientCache settings in web.config. No matter what I do, I can't seem to get it to cache the static content. This is a MVC5 app in VS2013. I've got all the static files in a folder "Assets".
The request looks like:
http://someserver/AppName/Assets/mainjs?v=FNj_9ZPAbYVAQsyDo2F8XUnWv5NQpY4iX2RGu4NpJ5g1
Attempt #1, place a new web.config in the Assets folder with the following:
<configuration>
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="30.00:00:00" />
</staticContent>
</system.webServer>
</configuration>
Attempt #2: place this following configuration in the root web.config
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00" />
</staticContent>
Attempt #3: trying setting the cache using the location tag
<location path="Assets">
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00" />
</staticContent>
</system.webServer>
</location>
Here are the things I've tried in IIS 8.5 manager. Under the Default Web Site/TestApp
HTTP Requeset Headers, Set Common HTTP Headers, check "Expire Web Content" "After 365 Day(s)".
Apply the same setting to the "Assets" folder
I've tried these steps each on their own and all together and every other which way. No matter what, it won't add the max-age value to Cache-Control.
For each of these, the browswer returns 200 responses for the CSS and JS bundles. I cannot get the server cache the content coming from the Assets folder.
Cache-Control:private
Content-Encoding:gzip
Content-Length:22591
Content-Type:text/css; charset=utf-8
Date:Mon, 02 Feb 2015 21:49:49 GMT
Expires:Tue, 02 Feb 2016 21:49:49 GMT
Last-Modified:Mon, 02 Feb 2015 21:49:49 GMT
Persistent-Auth:true
Server:Microsoft-IIS/8.5
Vary:Accept-Encoding
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET
Check the individual files of the bundle. What do the files look like when you turn off optimizations? After you register your Bundles make a call to :
BundleTable.EnableOptimizations = (!HttpContext.Current.IsDebuggingEnabled);
OR
BundleTable.EnableOptimizations = false;
It could be that each file is coming back with a not modified. Does the status change when you refresh while in a debug session?

Server.MapPath() with IIS URL Rewrite Module 2.0

I have a website in IIS, with a legacy classic asp application configured as a sub app.
I'm basically trying to create URL rewrite rules so that I don't have to change all of the relative URL's in the code.
e.g. URLS's such as "/themes/somedirectory" should be mapped to "/legacy/themes/somedirectory"
Using the URL Rewrite Module 2.0 I have a URL rewrite rule configured as follows:
<rule name="Reroute themes">
<match url="^themes.*" />
<action type="Rewrite" url="/legacy/{R:0}" />
</rule>
This works fine when navigating to the URL. However when using Server.MapPath(), it is not applying the rewrite rule.
Is Server.MapPath() actually supposed to take this into account? If not, how should I go about re-routing the application without modifying the code?
I was looking for the same thing so I gave it a try in a test app. It appears that Server.MapPath() does not acknowledge URL Rewrite Module rules.
Here is how I tested using an empty web project (Razor syntax):
Rewrite rule:
<system.webServer>
<rewrite>
<rules>
<rule name="Rewrite rule1 for test1">
<match url="^test1(.*)" />
<action type="Rewrite" url="{R:1}" appendQueryString="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
cshtml:
<p>
The URL for this page is #Request.Url.AbsoluteUri .
<br />
MapPath of /test1 is #Server.MapPath("~/test1")
<br />
MapPath of / is #Server.MapPath("~/")
</p>
I hit http://localhost/, then http://localhost/test1. The results were:
The URL for this page is http://localhost/ .
MapPath of /test1 is c:\src\temp\test1
MapPath of / is c:\src\temp\
So looks like mappath is basically taking System.AppDomain.CurrentDomain.BaseDirectory (or something similar) and combining it with the relative URL. On a side note, I have separately confirmed that MapPath() takes into account 1 level of virtual directory, but not a 2nd (ie. virt pointing to another location that also has a virt defined).
I just had this problem, and for now am going with create a special MapPath variant that corresponds to my rewrite rule.
So either something like this:
string MapTheme(string themeName)
{
return Path.Combine(Server.MapPath("/legacy"), themeName)
}
Or, if you prefer:
string MapThemePath(string themeUrl)
{
Match m = Regex.Match("^themes/(.*)");
if (!m.Success)
throw new ArgumentException();
string themeName = m.Groups[1].Value;
return Path.Combine(Server.MapPath("/legacy"), themeName)
}
Or generalize:
string MyMapPath(string url)
{
Match m = Regex.Match("^themes/(.*)");
if (m.Success)
{
string themeName = m.Groups[1].Value;
return Path.Combine(Server.MapPath("/legacy"), themeName)
}
else if (itsAnotherSpecialRewriteCase)
{
return doSomeSimilarTransformation();
}
// ...
else
{
// Handle non-rewritten URLs
return Server.MapPath(url);
}
}
I don't especially like this, because it violates "do not repeat yourself".

Resources