Last active
November 21, 2023 16:53
-
-
Save ericlaw1979/412648cd7bfe887920dd5479f952080a to your computer and use it in GitHub Desktop.
Trivial Fiddler extension that converts inbound images to JPEG.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Drawing; | |
using System.Drawing.Imaging; | |
using Fiddler; | |
using System.Windows.Forms; | |
using System.Diagnostics; | |
[assembly: Fiddler.RequiredVersion("2.6.2.0")] | |
namespace MakeJPEG | |
{ | |
public class JPEGify: IAutoTamper2 | |
{ | |
private bool bAutoConvert = false; | |
private long lngAutoJPEGQuality = 80; // 80% quality. | |
MenuItem oRulesMenuItem = new MenuItem("JPEGify I&mages..."); | |
MenuItem oContextMenuItem = new MenuItem("&JPEGify..."); | |
#region IAutoTamper Members | |
public void OnPeekAtResponseHeaders(Session oSession) | |
{ | |
if (!bAutoConvert) return; | |
if (!oSession.oResponse.headers.ExistsAndContains("Content-Type", "image/")) return; | |
// Disable streaming because we want to JPEG-convert stuff... | |
oSession.bBufferResponse = true; | |
} | |
public void AutoTamperResponseBefore(Session oSession) | |
{ | |
if (!bAutoConvert) return; | |
ProcessSession(oSession, lngAutoJPEGQuality); | |
} | |
private void ProcessSession(Session oSession, long lngQuality) | |
{ | |
if (oSession.responseCode != 200 || !oSession.bHasResponse || | |
!oSession.oResponse.headers.ExistsAndContains("Content-Type", "image/")) return; | |
if (!oSession.oResponse.headers.ExistsAndContains("Content-Type", "gif") && // <-- Should we bail if animated | |
!oSession.oResponse.headers.ExistsAndContains("Content-Type", "png") && | |
!oSession.oResponse.headers.ExistsAndContains("Content-Type", "bmp") && | |
!oSession.oResponse.headers.ExistsAndContains("Content-Type", "jp")) | |
return; | |
try | |
{ | |
int iOldSize = oSession.responseBodyBytes.Length; | |
MakeJPEGOfQuality(oSession, lngQuality); | |
oSession.oFlags.Add("Size-Before-JPEGify", iOldSize.ToString("N0")); | |
float flChange = 100 * (oSession.responseBodyBytes.Length / (float)iOldSize); | |
string sBloatInfo = String.Format("JPEGify to {0:N0} from {1:N0} bytes ({2:N1}%)", oSession.responseBodyBytes.Length, iOldSize, flChange); | |
FiddlerApplication.UI.SetStatusText(sBloatInfo); | |
} | |
catch (Exception eX) | |
{ | |
FiddlerApplication.Log.LogString(eX.ToString()); | |
} | |
} | |
private void MakeJPEGOfQuality(Session oSession, long lngQuality) | |
{ | |
if ((lngQuality < 1) || (lngQuality > 100)) throw new ArgumentOutOfRangeException("lngQuality", lngQuality, "Quality must be between 1 and 100"); | |
oSession.utilDecodeResponse(); | |
using (MemoryStream oStream = new MemoryStream(oSession.responseBodyBytes)) | |
{ | |
Bitmap oBMP = new Bitmap(oStream); | |
using (MemoryStream oNewStream = new MemoryStream()) | |
{ | |
// TODO: Can we cache this in a thread-safe manner? | |
ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg); | |
// Create an Encoder object based on the GUID for the Quality parameter category. | |
Encoder myEncoder = Encoder.Quality; | |
EncoderParameters myEncoderParameters = new EncoderParameters(1); | |
EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, lngQuality); | |
myEncoderParameters.Param[0] = myEncoderParameter; | |
oBMP.Save(oNewStream, jgpEncoder, myEncoderParameters); | |
oSession.ResponseBody = oNewStream.ToArray(); | |
oSession.oResponse.headers["Content-Type"] = "image/jpeg"; | |
oSession.oResponse.headers["Cache-Control"] = "no-cache"; | |
} | |
} | |
} | |
public void OnBeforeReturningError(Session oSession) {/* no-op */} | |
public void AutoTamperRequestAfter(Session oSession) {/* no-op */} | |
public void AutoTamperRequestBefore(Session oSession){/* no-op */} | |
public void AutoTamperResponseAfter(Session oSession){/* no-op */} | |
#endregion | |
private static ImageCodecInfo GetEncoder(ImageFormat format) | |
{ | |
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders(); | |
foreach (ImageCodecInfo codec in codecs) | |
{ | |
if (codec.FormatID == format.Guid) | |
{ | |
return codec; | |
} | |
} | |
return null; | |
} | |
#region IFiddlerExtension Members | |
public void OnBeforeUnload() | |
{ | |
bAutoConvert = false; | |
oRulesMenuItem.Dispose(); | |
oContextMenuItem.Dispose(); | |
} | |
public void OnLoad() | |
{ | |
oRulesMenuItem.Click += delegate(object s, EventArgs e) | |
{ | |
bool bNewValue = !oRulesMenuItem.Checked; | |
if (bNewValue) | |
{ | |
string sQuality = frmPrompt.GetUserString("Set Quality", "Enter a value, 1 to 100", lngAutoJPEGQuality.ToString(), true, frmPrompt.PromptIcon.Numbers); | |
if (String.IsNullOrEmpty(sQuality)) | |
{ | |
bNewValue = false; | |
} | |
if (bNewValue) | |
{ | |
long lng = 0; | |
if (long.TryParse(sQuality, out lng)) | |
{ | |
lngAutoJPEGQuality = lng; | |
} | |
else | |
{ | |
bNewValue = false; | |
} | |
} | |
} | |
oRulesMenuItem.Checked = bAutoConvert = bNewValue; | |
}; | |
oContextMenuItem.Click += delegate(object s, EventArgs e) | |
{ | |
string sQuality = frmPrompt.GetUserString("Set Quality", "Enter a value, 1 to 100", lngAutoJPEGQuality.ToString(), true, frmPrompt.PromptIcon.Numbers); | |
if (String.IsNullOrEmpty(sQuality)) return; | |
long lng = 0; | |
if (!long.TryParse(sQuality, out lng) || (lng > 100) || (lng < 1)) return; | |
foreach (Session oS in FiddlerApplication.UI.GetSelectedSessions()) | |
{ | |
ProcessSession(oS, lng); | |
oS.RefreshUI(); | |
} | |
FiddlerApplication.UI.SetStatusText("JPEGify Complete"); | |
}; | |
FiddlerApplication.UI.mnuRules.MenuItems.Add(oRulesMenuItem); | |
FiddlerApplication.UI.lvSessions.ContextMenu.MenuItems.Add(0, oContextMenuItem ); | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
csc /target:library /r:"C:\Users\ericlaw\AppData\Local\Programs\Fiddler\fiddler.exe" jpegify.cs