Large updates to webserver

Do not merge yet

Change-Id: I38c56844c1b0e3e8e5493c2705e62e6db7ee2102
diff --git a/include/aspeed/JTABLES.H b/include/aspeed/JTABLES.H
index bff39e3..8f2d9f3 100644
--- a/include/aspeed/JTABLES.H
+++ b/include/aspeed/JTABLES.H
@@ -18,15 +18,15 @@
 

 // Standard Huffman tables (cf. JPEG standard section K.3) */

 

-static const unsigned char std_dc_luminance_nrcodes[17] = {0, 0, 1, 5, 1, 1, 1, 1, 1,

-                                                     1, 0, 0, 0, 0, 0, 0, 0};

+static const unsigned char std_dc_luminance_nrcodes[17] = {

+    0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0};

 static const unsigned char std_dc_luminance_values[12] = {0, 1, 2, 3, 4,  5,

-                                                    6, 7, 8, 9, 10, 11};

+                                                          6, 7, 8, 9, 10, 11};

 

 static const unsigned char std_dc_chrominance_nrcodes[17] = {

     0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};

 static const unsigned char std_dc_chrominance_values[12] = {0, 1, 2, 3, 4,  5,

-                                                      6, 7, 8, 9, 10, 11};

+                                                            6, 7, 8, 9, 10, 11};

 

 static const unsigned char std_ac_luminance_nrcodes[17] = {

     0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d};

@@ -284,7 +284,7 @@
     185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185,

     185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185};

 

-typedef struct {

+struct Huffman_table {

   unsigned char Length[17];  // k =1-16 ; L[k] indicates the number of Huffman

                              // codes of length k

   unsigned short int minor_code[17];  // indicates the value of the smallest

@@ -296,4 +296,4 @@
   // Low nibble = size (in bits) of the coefficient which will be taken from the

   // data stream

   unsigned char Len[65536];

-} Huffman_table;

+};

diff --git a/include/ast_jpeg_decoder.hpp b/include/ast_jpeg_decoder.hpp
index b5144ab..d2a482a 100644
--- a/include/ast_jpeg_decoder.hpp
+++ b/include/ast_jpeg_decoder.hpp
@@ -1,41 +1,23 @@
 #pragma once
 
-#include <string.h>
+#include <ast_video_types.hpp>
 #include <array>
 #include <aspeed/JTABLES.H>
-#include <ast_video_types.hpp>
 #include <cassert>
 #include <cstdint>
+#include <cstring>
 #include <iostream>
 #include <vector>
 
-/*
-template <class T, class Compare>
-constexpr const T &clamp(const T &v, const T &lo, const T &hi, Compare comp) {
-  return assert(!comp(hi, lo)), comp(v, lo) ? lo : comp(hi, v) ? hi : v;
-}
-
-template <class T>
-constexpr const T &clamp(const T &v, const T &lo, const T &hi) {
-  return clamp(v, lo, hi, std::less<>());
-}
-*/
 namespace AstVideo {
 
 struct COLOR_CACHE {
-  COLOR_CACHE() {
-    for (int i = 0; i < 4; i++) {
-      Index[i] = i;
-    }
-    Color[0] = 0x008080;
-    Color[1] = 0xFF8080;
-    Color[2] = 0x808080;
-    Color[3] = 0xC08080;
-  }
+  COLOR_CACHE()
+      : Color{0x008080, 0xFF8080, 0x808080, 0xC08080}, Index{0, 1, 2, 3} {}
 
   unsigned long Color[4];
   unsigned char Index[4];
-  unsigned char BitMapBits;
+  unsigned char BitMapBits{};
 };
 
 struct RGB {
@@ -94,7 +76,7 @@
     float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f,
                             1.0f, 0.785694958f, 0.541196100f, 0.275899379f};
     uint8_t j, row, col;
-    uint8_t tempQT[64];
+    std::array<uint8_t, 64> tempQT{};
 
     // Load quantization coefficients from JPG file, scale them for DCT and
     // reorder
@@ -125,17 +107,20 @@
         std_luminance_qt = Tbl_100Y;
         break;
     }
-    set_quant_table(std_luminance_qt, (uint8_t)SCALEFACTOR, tempQT);
+    set_quant_table(std_luminance_qt, static_cast<uint8_t>(SCALEFACTOR),
+                    tempQT);
 
-    for (j = 0; j <= 63; j++) quant_table[j] = tempQT[zigzag[j]];
+    for (j = 0; j <= 63; j++) {
+      quant_table[j] = tempQT[zigzag[j]];
+    }
     j = 0;
-    for (row = 0; row <= 7; row++)
+    for (row = 0; row <= 7; row++) {
       for (col = 0; col <= 7; col++) {
-        quant_table[j] =
-            (long)((quant_table[j] * scalefactor[row] * scalefactor[col]) *
-                   65536);
+        quant_table[j] = static_cast<long>(
+            (quant_table[j] * scalefactor[row] * scalefactor[col]) * 65536);
         j++;
       }
+    }
     byte_pos += 64;
   }
 
@@ -143,7 +128,7 @@
     float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f,
                             1.0f, 0.785694958f, 0.541196100f, 0.275899379f};
     uint8_t j, row, col;
-    uint8_t tempQT[64];
+    std::array<uint8_t, 64> tempQT{};
 
     // Load quantization coefficients from JPG file, scale them for DCT and
     // reorder from zig-zag order
@@ -202,7 +187,8 @@
           break;
       }
     }
-    set_quant_table(std_chrominance_qt, (uint8_t)SCALEFACTORUV, tempQT);
+    set_quant_table(std_chrominance_qt, static_cast<uint8_t>(SCALEFACTORUV),
+                    tempQT);
 
     for (j = 0; j <= 63; j++) {
       quant_table[j] = tempQT[zigzag[j]];
@@ -210,9 +196,8 @@
     j = 0;
     for (row = 0; row <= 7; row++) {
       for (col = 0; col <= 7; col++) {
-        quant_table[j] =
-            (long)((quant_table[j] * scalefactor[row] * scalefactor[col]) *
-                   65536);
+        quant_table[j] = static_cast<long>(
+            (quant_table[j] * scalefactor[row] * scalefactor[col]) * 65536);
         j++;
       }
     }
@@ -223,7 +208,7 @@
     float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f,
                             1.0f, 0.785694958f, 0.541196100f, 0.275899379f};
     uint8_t j, row, col;
-    uint8_t tempQT[64];
+    std::array<uint8_t, 64> tempQT{};
 
     // Load quantization coefficients from JPG file, scale them for DCT and
     // reorder
@@ -255,17 +240,20 @@
         break;
     }
     //  Note: pass ADVANCE SCALE FACTOR to sub-function in Dual-JPEG
-    set_quant_table(std_luminance_qt, (uint8_t)ADVANCESCALEFACTOR, tempQT);
+    set_quant_table(std_luminance_qt, static_cast<uint8_t>(ADVANCESCALEFACTOR),
+                    tempQT);
 
-    for (j = 0; j <= 63; j++) quant_table[j] = tempQT[zigzag[j]];
+    for (j = 0; j <= 63; j++) {
+      quant_table[j] = tempQT[zigzag[j]];
+    }
     j = 0;
-    for (row = 0; row <= 7; row++)
+    for (row = 0; row <= 7; row++) {
       for (col = 0; col <= 7; col++) {
-        quant_table[j] =
-            (long)((quant_table[j] * scalefactor[row] * scalefactor[col]) *
-                   65536);
+        quant_table[j] = static_cast<long>(
+            (quant_table[j] * scalefactor[row] * scalefactor[col]) * 65536);
         j++;
       }
+    }
     byte_pos += 64;
   }
 
@@ -274,7 +262,7 @@
     float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f,
                             1.0f, 0.785694958f, 0.541196100f, 0.275899379f};
     uint8_t j, row, col;
-    uint8_t tempQT[64];
+    std::array<uint8_t, 64> tempQT{};
 
     // Load quantization coefficients from JPG file, scale them for DCT and
     // reorder
@@ -335,17 +323,20 @@
       }
     }
     //  Note: pass ADVANCE SCALE FACTOR to sub-function in Dual-JPEG
-    set_quant_table(std_chrominance_qt, (uint8_t)ADVANCESCALEFACTORUV, tempQT);
+    set_quant_table(std_chrominance_qt,
+                    static_cast<uint8_t>(ADVANCESCALEFACTORUV), tempQT);
 
-    for (j = 0; j <= 63; j++) quant_table[j] = tempQT[zigzag[j]];
+    for (j = 0; j <= 63; j++) {
+      quant_table[j] = tempQT[zigzag[j]];
+    }
     j = 0;
-    for (row = 0; row <= 7; row++)
+    for (row = 0; row <= 7; row++) {
       for (col = 0; col <= 7; col++) {
-        quant_table[j] =
-            (long)((quant_table[j] * scalefactor[row] * scalefactor[col]) *
-                   65536);
+        quant_table[j] = static_cast<long>(
+            (quant_table[j] * scalefactor[row] * scalefactor[col]) * 65536);
         j++;
       }
+    }
     byte_pos += 64;
   }
 
@@ -387,7 +378,8 @@
            inptr[DCTSIZE * 4] | inptr[DCTSIZE * 5] | inptr[DCTSIZE * 6] |
            inptr[DCTSIZE * 7]) == 0) {
         /* AC terms all zero */
-        dcval = (int)((inptr[DCTSIZE * 0] * quantptr[DCTSIZE * 0]) >> 16);
+        dcval = static_cast<int>((inptr[DCTSIZE * 0] * quantptr[DCTSIZE * 0]) >>
+                                 16);
 
         wsptr[DCTSIZE * 0] = dcval;
         wsptr[DCTSIZE * 1] = dcval;
@@ -445,14 +437,14 @@
       tmp5 = tmp11 - tmp6;
       tmp4 = tmp10 + tmp5;
 
-      wsptr[DCTSIZE * 0] = (int)(tmp0 + tmp7);
-      wsptr[DCTSIZE * 7] = (int)(tmp0 - tmp7);
-      wsptr[DCTSIZE * 1] = (int)(tmp1 + tmp6);
-      wsptr[DCTSIZE * 6] = (int)(tmp1 - tmp6);
-      wsptr[DCTSIZE * 2] = (int)(tmp2 + tmp5);
-      wsptr[DCTSIZE * 5] = (int)(tmp2 - tmp5);
-      wsptr[DCTSIZE * 4] = (int)(tmp3 + tmp4);
-      wsptr[DCTSIZE * 3] = (int)(tmp3 - tmp4);
+      wsptr[DCTSIZE * 0] = (tmp0 + tmp7);
+      wsptr[DCTSIZE * 7] = (tmp0 - tmp7);
+      wsptr[DCTSIZE * 1] = (tmp1 + tmp6);
+      wsptr[DCTSIZE * 6] = (tmp1 - tmp6);
+      wsptr[DCTSIZE * 2] = (tmp2 + tmp5);
+      wsptr[DCTSIZE * 5] = (tmp2 - tmp5);
+      wsptr[DCTSIZE * 4] = (tmp3 + tmp4);
+      wsptr[DCTSIZE * 3] = (tmp3 - tmp4);
 
       inptr++; /* advance pointers to next column */
       quantptr++;
@@ -465,7 +457,7 @@
 
 //#define RANGE_MASK 1023; //2 bits wider than legal samples
 #define PASS1_BITS 0
-#define IDESCALE(x, n) ((int)((x) >> n))
+#define IDESCALE(x, n) ((int)((x) >> (n)))
 
     wsptr = workspace;
     for (ctr = 0; ctr < DCTSIZE; ctr++) {
@@ -480,10 +472,10 @@
       */
       /* Even part */
 
-      tmp10 = ((int)wsptr[0] + (int)wsptr[4]);
-      tmp11 = ((int)wsptr[0] - (int)wsptr[4]);
+      tmp10 = (wsptr[0] + wsptr[4]);
+      tmp11 = (wsptr[0] - wsptr[4]);
 
-      tmp13 = ((int)wsptr[2] + (int)wsptr[6]);
+      tmp13 = (wsptr[2] + wsptr[6]);
       tmp12 = MULTIPLY((int)wsptr[2] - (int)wsptr[6], FIX_1_414213562) - tmp13;
 
       tmp0 = tmp10 + tmp13;
@@ -493,10 +485,10 @@
 
       /* Odd part */
 
-      z13 = (int)wsptr[5] + (int)wsptr[3];
-      z10 = (int)wsptr[5] - (int)wsptr[3];
-      z11 = (int)wsptr[1] + (int)wsptr[7];
-      z12 = (int)wsptr[1] - (int)wsptr[7];
+      z13 = wsptr[5] + wsptr[3];
+      z10 = wsptr[5] - wsptr[3];
+      z11 = wsptr[1] + wsptr[7];
+      z12 = wsptr[1] - wsptr[7];
 
       tmp7 = z11 + z13;                             /* phase 5 */
       tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */
@@ -538,7 +530,7 @@
     int nBlocksInMcu = 6;
     unsigned int pixel_x, pixel_y;
 
-    pByte = (struct RGB *)pBgr;
+    pByte = reinterpret_cast<struct RGB *>(pBgr);
     if (yuvmode == YuvMode::YUV444) {
       py = pYCbCr;
       pcb = pYCbCr + 64;
@@ -566,7 +558,9 @@
         pos += WIDTH;
       }
     } else {
-      for (i = 0; i < nBlocksInMcu - 2; i++) py420[i] = pYCbCr + i * 64;
+      for (i = 0; i < nBlocksInMcu - 2; i++) {
+        py420[i] = pYCbCr + i * 64;
+      }
       pcb = pYCbCr + (nBlocksInMcu - 2) * 64;
       pcr = pcb + 64;
 
@@ -606,7 +600,7 @@
     int nBlocksInMcu = 6;
     unsigned int pixel_x, pixel_y;
 
-    pByte = (struct RGB *)pBgr;
+    pByte = reinterpret_cast<struct RGB *>(pBgr);
     if (yuvmode == YuvMode::YUV444) {
       py = pYCbCr;
       pcb = pYCbCr + 64;
@@ -633,7 +627,9 @@
         pos += WIDTH;
       }
     } else {
-      for (i = 0; i < nBlocksInMcu - 2; i++) py420[i] = pYCbCr + i * 64;
+      for (i = 0; i < nBlocksInMcu - 2; i++) {
+        py420[i] = pYCbCr + i * 64;
+      }
       pcb = pYCbCr + (nBlocksInMcu - 2) * 64;
       pcr = pcb + 64;
 
@@ -697,7 +693,8 @@
 
     //    YUVToRGB (txb, tyb, byTileYuv, (unsigned char *)outBuf);
     //  YUVBuffer for YUV record
-    YUVToRGB(txb, tyb, byTileYuv, YUVBuffer.data(), (unsigned char *)outBuf);
+    YUVToRGB(txb, tyb, byTileYuv, YUVBuffer.data(),
+             reinterpret_cast<unsigned char *>(outBuf));
   }
 
   void Decompress_2PASS(int txb, int tyb, char *outBuf,
@@ -718,7 +715,8 @@
     process_Huffman_data_unit(CrDC_nr, CrAC_nr, &DCCr, 128);
     IDCT_transform(DCT_coeff + 128, ptr, QT_TableSelection + 1);
 
-    YUVToBuffer(txb, tyb, byTileYuv, YUVBuffer.data(), (unsigned char *)outBuf);
+    YUVToBuffer(txb, tyb, byTileYuv, YUVBuffer.data(),
+                reinterpret_cast<unsigned char *>(outBuf));
     //    YUVToRGB (txb, tyb, byTileYuv, (unsigned char *)outBuf);
   }
 
@@ -738,7 +736,7 @@
       }
     } else {
       for (i = 0; i < 64; i++) {
-        Data = (int)lookKbits(VQ->BitMapBits);
+        Data = static_cast<int>(lookKbits(VQ->BitMapBits));
         ptr[0] = (VQ->Color[VQ->Index[Data]] & 0xFF0000) >> 16;
         ptr[64] = (VQ->Color[VQ->Index[Data]] & 0x00FF00) >> 8;
         ptr[128] = VQ->Color[VQ->Index[Data]] & 0x0000FF;
@@ -747,22 +745,27 @@
       }
     }
     //    YUVToRGB (txb, tyb, byTileYuv, (unsigned char *)outBuf);
-    YUVToRGB(txb, tyb, byTileYuv, YUVBuffer.data(), (unsigned char *)outBuf);
+    YUVToRGB(txb, tyb, byTileYuv, YUVBuffer.data(),
+             reinterpret_cast<unsigned char *>(outBuf));
   }
 
-  void MoveBlockIndex(void) {
+  void MoveBlockIndex() {
     if (yuvmode == YuvMode::YUV444) {
       txb++;
-      if (txb >= (int)(WIDTH / 8)) {
+      if (txb >= static_cast<int>(WIDTH / 8)) {
         tyb++;
-        if (tyb >= (int)(HEIGHT / 8)) tyb = 0;
+        if (tyb >= static_cast<int>(HEIGHT / 8)) {
+          tyb = 0;
+        }
         txb = 0;
       }
     } else {
       txb++;
-      if (txb >= (int)(WIDTH / 16)) {
+      if (txb >= static_cast<int>(WIDTH / 16)) {
         tyb++;
-        if (tyb >= (int)(HEIGHT / 16)) tyb = 0;
+        if (tyb >= static_cast<int>(HEIGHT / 16)) {
+          tyb = 0;
+        }
         txb = 0;
       }
     }
@@ -782,13 +785,13 @@
     /* Cr=>G value is scaled-up -0.8125 * x */
     /* Cb=>G value is scaled-up -0.390625 * x */
     for (i = 0, x = -128; i < 256; i++, x++) {
-      m_CrToR[i] = (int)(FIX(1.597656) * x + nHalf) >> 16;
-      m_CbToB[i] = (int)(FIX(2.015625) * x + nHalf) >> 16;
-      m_CrToG[i] = (int)(-FIX(0.8125) * x + nHalf) >> 16;
-      m_CbToG[i] = (int)(-FIX(0.390625) * x + nHalf) >> 16;
+      m_CrToR[i] = (FIX(1.597656) * x + nHalf) >> 16;
+      m_CbToB[i] = (FIX(2.015625) * x + nHalf) >> 16;
+      m_CrToG[i] = (-FIX(0.8125) * x + nHalf) >> 16;
+      m_CbToG[i] = (-FIX(0.390625) * x + nHalf) >> 16;
     }
     for (i = 0, x = -16; i < 256; i++, x++) {
-      m_Y[i] = (int)(FIX(1.164) * x + nHalf) >> 16;
+      m_Y[i] = (FIX(1.164) * x + nHalf) >> 16;
     }
     // For Color Text Enchance Y Re-map. Recommend to disable in default
     /*
@@ -823,17 +826,20 @@
     for (j = 1; j <= 16; j++) {
       HT->Length[j] = nrcode[j];
     }
-    for (i = 0, k = 1; k <= 16; k++)
+    for (i = 0, k = 1; k <= 16; k++) {
       for (j = 0; j < HT->Length[k]; j++) {
         HT->V[WORD_hi_lo(k, j)] = value[i];
         i++;
       }
+    }
 
     code = 0;
     for (k = 1; k <= 16; k++) {
-      HT->minor_code[k] = (unsigned short int)code;
-      for (j = 1; j <= HT->Length[k]; j++) code++;
-      HT->major_code[k] = (unsigned short int)(code - 1);
+      HT->minor_code[k] = static_cast<unsigned short int>(code);
+      for (j = 1; j <= HT->Length[k]; j++) {
+        code++;
+      }
+      HT->major_code[k] = static_cast<unsigned short int>(code - 1);
       code *= 2;
       if (HT->Length[k] == 0) {
         HT->minor_code[k] = 0xFFFF;
@@ -846,10 +852,10 @@
 
     for (code_index = 1; code_index < 65535; code_index++) {
       if (code_index < Huff_code[i]) {
-        HT->Len[code_index] = (unsigned char)Huff_code[i + 1];
+        HT->Len[code_index] = static_cast<unsigned char>(Huff_code[i + 1]);
       } else {
         i = i + 2;
-        HT->Len[code_index] = (unsigned char)Huff_code[i + 1];
+        HT->Len[code_index] = static_cast<unsigned char>(Huff_code[i + 1]);
       }
     }
   }
@@ -870,18 +876,24 @@
   /* Allocate and fill in the sample_range_limit table */
   {
     int j;
-    rlimit_table = (unsigned char *)malloc(5 * 256L + 128);
+    rlimit_table = reinterpret_cast<unsigned char *>(malloc(5 * 256L + 128));
     /* First segment of "simple" table: limit[x] = 0 for x < 0 */
     memset((void *)rlimit_table, 0, 256);
     rlimit_table += 256; /* allow negative subscripts of simple table */
     /* Main part of "simple" table: limit[x] = x */
-    for (j = 0; j < 256; j++) rlimit_table[j] = j;
+    for (j = 0; j < 256; j++) {
+      rlimit_table[j] = j;
+    }
     /* End of simple table, rest of first half of post-IDCT table */
-    for (j = 256; j < 640; j++) rlimit_table[j] = 255;
+    for (j = 256; j < 640; j++) {
+      rlimit_table[j] = 255;
+    }
 
     /* Second half of post-IDCT table */
     memset((void *)(rlimit_table + 640), 0, 384);
-    for (j = 0; j < 128; j++) rlimit_table[j + 1024] = j;
+    for (j = 0; j < 128; j++) {
+      rlimit_table[j + 1024] = j;
+    }
   }
 
   inline unsigned short int WORD_hi_lo(uint8_t byte_high, uint8_t byte_low) {
@@ -905,15 +917,16 @@
     huff_values = HTDC[DC_nr].V;
 
     // DC
-    k = HTDC[DC_nr].Len[(unsigned short int)(codebuf >> 16)];
+    k = HTDC[DC_nr].Len[static_cast<unsigned short int>(codebuf >> 16)];
     // river
     //	 tmp_Hcode=lookKbits(k);
-    tmp_Hcode = (unsigned short int)(codebuf >> (32 - k));
+    tmp_Hcode = static_cast<unsigned short int>(codebuf >> (32 - k));
     skipKbits(k);
-    size_val = huff_values[WORD_hi_lo(k, (uint8_t)(tmp_Hcode - min_code[k]))];
-    if (size_val == 0)
+    size_val = huff_values[WORD_hi_lo(
+        k, static_cast<uint8_t>(tmp_Hcode - min_code[k]))];
+    if (size_val == 0) {
       DCT_coeff[position + 0] = *previous_DC;
-    else {
+    } else {
       DCT_coeff[position + 0] = *previous_DC + getKbits(size_val);
       *previous_DC = DCT_coeff[position + 0];
     }
@@ -925,12 +938,12 @@
 
     nr = 1;  // AC coefficient
     do {
-      k = HTAC[AC_nr].Len[(unsigned short int)(codebuf >> 16)];
-      tmp_Hcode = (unsigned short int)(codebuf >> (32 - k));
+      k = HTAC[AC_nr].Len[static_cast<unsigned short int>(codebuf >> 16)];
+      tmp_Hcode = static_cast<unsigned short int>(codebuf >> (32 - k));
       skipKbits(k);
 
-      byte_temp =
-          huff_values[WORD_hi_lo(k, (uint8_t)(tmp_Hcode - min_code[k]))];
+      byte_temp = huff_values[WORD_hi_lo(
+          k, static_cast<uint8_t>(tmp_Hcode - min_code[k]))];
       size_val = byte_temp & 0xF;
       count_0 = byte_temp >> 4;
       if (size_val == 0) {
@@ -948,7 +961,7 @@
   unsigned short int lookKbits(uint8_t k) {
     unsigned short int revcode;
 
-    revcode = (unsigned short int)(codebuf >> (32 - k));
+    revcode = static_cast<unsigned short int>(codebuf >> (32 - k));
 
     return (revcode);
   }
@@ -975,7 +988,7 @@
 
     // river
     // signed_wordvalue=lookKbits(k);
-    signed_wordvalue = (unsigned short int)(codebuf >> (32 - k));
+    signed_wordvalue = static_cast<unsigned short int>(codebuf >> (32 - k));
     if (((1L << (k - 1)) & signed_wordvalue) == 0) {
       // neg_pow2 was previously defined as the below.  It seemed silly to keep
       // a table of values around for something
@@ -1002,17 +1015,21 @@
   }
 
   void set_quant_table(const uint8_t *basic_table, uint8_t scale_factor,
-                       uint8_t *newtable)
+                       std::array<uint8_t, 64>& newtable)
   // Set quantization table and zigzag reorder it
   {
     uint8_t i;
     long temp;
     for (i = 0; i < 64; i++) {
-      temp = ((long)(basic_table[i] * 16) / scale_factor);
+      temp = (static_cast<long>(basic_table[i] * 16) / scale_factor);
       /* limit the values to the valid range */
-      if (temp <= 0L) temp = 1L;
-      if (temp > 255L) temp = 255L; /* limit to baseline range if requested */
-      newtable[zigzag[i]] = (uint8_t)temp;
+      if (temp <= 0L) {
+        temp = 1L;
+      }
+      if (temp > 255L) {
+        temp = 255L; /* limit to baseline range if requested */
+      }
+      newtable[zigzag[i]] = static_cast<uint8_t>(temp);
     }
   }
 
@@ -1053,17 +1070,17 @@
       Mapping = 0;           // 0 or 1
 
       if (yuvmode == YuvMode::YUV420) {
-        if (WIDTH % 16) {
+        if ((WIDTH % 16) != 0u) {
           WIDTH = WIDTH + 16 - (WIDTH % 16);
         }
-        if (HEIGHT % 16) {
+        if ((HEIGHT % 16) != 0u) {
           HEIGHT = HEIGHT + 16 - (HEIGHT % 16);
         }
       } else {
-        if (WIDTH % 8) {
+        if ((WIDTH % 8) != 0u) {
           WIDTH = WIDTH + 8 - (WIDTH % 8);
         }
-        if (HEIGHT % 8) {
+        if ((HEIGHT % 8) != 0u) {
           HEIGHT = HEIGHT + 8 - (HEIGHT % 8);
         }
       }
@@ -1098,7 +1115,7 @@
         case JpgBlock::JPEG_NO_SKIP_CODE:
           updatereadbuf(&codebuf, &newbuf, BLOCK_AST2100_START_LENGTH, &newbits,
                         buffer);
-          Decompress(txb, tyb, (char *)OutBuffer.data(), 0);
+          Decompress(txb, tyb, reinterpret_cast<char *>(OutBuffer.data()), 0);
           break;
         case JpgBlock::FRAME_END_CODE:
           return 0;
@@ -1110,7 +1127,7 @@
 
           updatereadbuf(&codebuf, &newbuf, BLOCK_AST2100_SKIP_LENGTH, &newbits,
                         buffer);
-          Decompress(txb, tyb, (char *)OutBuffer.data(), 0);
+          Decompress(txb, tyb, reinterpret_cast<char *>(OutBuffer.data()), 0);
           break;
         case JpgBlock::VQ_NO_SKIP_1_COLOR_CODE:
           updatereadbuf(&codebuf, &newbuf, BLOCK_AST2100_START_LENGTH, &newbits,
@@ -1129,7 +1146,8 @@
                             buffer);
             }
           }
-          VQ_Decompress(txb, tyb, (char *)OutBuffer.data(), 0, &Decode_Color);
+          VQ_Decompress(txb, tyb, reinterpret_cast<char *>(OutBuffer.data()), 0,
+                        &Decode_Color);
           break;
         case JpgBlock::VQ_SKIP_1_COLOR_CODE:
           txb = (codebuf & 0x0FF00000) >> 20;
@@ -1151,7 +1169,8 @@
                             buffer);
             }
           }
-          VQ_Decompress(txb, tyb, (char *)OutBuffer.data(), 0, &Decode_Color);
+          VQ_Decompress(txb, tyb, reinterpret_cast<char *>(OutBuffer.data()), 0,
+                        &Decode_Color);
           break;
 
         case JpgBlock::VQ_NO_SKIP_2_COLOR_CODE:
@@ -1171,7 +1190,8 @@
                             buffer);
             }
           }
-          VQ_Decompress(txb, tyb, (char *)OutBuffer.data(), 0, &Decode_Color);
+          VQ_Decompress(txb, tyb, reinterpret_cast<char *>(OutBuffer.data()), 0,
+                        &Decode_Color);
           break;
         case JpgBlock::VQ_SKIP_2_COLOR_CODE:
           txb = (codebuf & 0x0FF00000) >> 20;
@@ -1193,7 +1213,8 @@
                             buffer);
             }
           }
-          VQ_Decompress(txb, tyb, (char *)OutBuffer.data(), 0, &Decode_Color);
+          VQ_Decompress(txb, tyb, reinterpret_cast<char *>(OutBuffer.data()), 0,
+                        &Decode_Color);
 
           break;
         case JpgBlock::VQ_NO_SKIP_4_COLOR_CODE:
@@ -1201,19 +1222,19 @@
                         buffer);
           Decode_Color.BitMapBits = 2;
 
-          for (int i = 0; i < 4; i++) {
-            Decode_Color.Index[i] = ((codebuf >> 29) & VQ_INDEX_MASK);
+          for (unsigned char &i : Decode_Color.Index) {
+            i = ((codebuf >> 29) & VQ_INDEX_MASK);
             if (((codebuf >> 31) & VQ_HEADER_MASK) == VQ_NO_UPDATE_HEADER) {
               updatereadbuf(&codebuf, &newbuf, VQ_NO_UPDATE_LENGTH, &newbits,
                             buffer);
             } else {
-              Decode_Color.Color[Decode_Color.Index[i]] =
-                  ((codebuf >> 5) & VQ_COLOR_MASK);
+              Decode_Color.Color[i] = ((codebuf >> 5) & VQ_COLOR_MASK);
               updatereadbuf(&codebuf, &newbuf, VQ_UPDATE_LENGTH, &newbits,
                             buffer);
             }
           }
-          VQ_Decompress(txb, tyb, (char *)OutBuffer.data(), 0, &Decode_Color);
+          VQ_Decompress(txb, tyb, reinterpret_cast<char *>(OutBuffer.data()), 0,
+                        &Decode_Color);
 
           break;
 
@@ -1225,19 +1246,19 @@
                         buffer);
           Decode_Color.BitMapBits = 2;
 
-          for (int i = 0; i < 4; i++) {
-            Decode_Color.Index[i] = ((codebuf >> 29) & VQ_INDEX_MASK);
+          for (unsigned char &i : Decode_Color.Index) {
+            i = ((codebuf >> 29) & VQ_INDEX_MASK);
             if (((codebuf >> 31) & VQ_HEADER_MASK) == VQ_NO_UPDATE_HEADER) {
               updatereadbuf(&codebuf, &newbuf, VQ_NO_UPDATE_LENGTH, &newbits,
                             buffer);
             } else {
-              Decode_Color.Color[Decode_Color.Index[i]] =
-                  ((codebuf >> 5) & VQ_COLOR_MASK);
+              Decode_Color.Color[i] = ((codebuf >> 5) & VQ_COLOR_MASK);
               updatereadbuf(&codebuf, &newbuf, VQ_UPDATE_LENGTH, &newbits,
                             buffer);
             }
           }
-          VQ_Decompress(txb, tyb, (char *)OutBuffer.data(), 0, &Decode_Color);
+          VQ_Decompress(txb, tyb, reinterpret_cast<char *>(OutBuffer.data()), 0,
+                        &Decode_Color);
 
           break;
         case JpgBlock::JPEG_SKIP_PASS2_CODE:
@@ -1246,7 +1267,8 @@
 
           updatereadbuf(&codebuf, &newbuf, BLOCK_AST2100_SKIP_LENGTH, &newbits,
                         buffer);
-          Decompress_2PASS(txb, tyb, (char *)OutBuffer.data(), 2);
+          Decompress_2PASS(txb, tyb, reinterpret_cast<char *>(OutBuffer.data()),
+                           2);
 
           break;
         default:
@@ -1277,41 +1299,41 @@
 #endif
 
  private:
-  YuvMode yuvmode;
+  YuvMode yuvmode{};
   // WIDTH and HEIGHT are the modes your display used
-  unsigned long WIDTH;
-  unsigned long HEIGHT;
-  unsigned long USER_WIDTH;
-  unsigned long USER_HEIGHT;
-  unsigned char Y_selector;
+  unsigned long WIDTH{};
+  unsigned long HEIGHT{};
+  unsigned long USER_WIDTH{};
+  unsigned long USER_HEIGHT{};
+  unsigned char Y_selector{};
   int SCALEFACTOR;
   int SCALEFACTORUV;
   int ADVANCESCALEFACTOR;
   int ADVANCESCALEFACTORUV;
-  int Mapping;
-  unsigned char UV_selector;
-  unsigned char advance_selector;
-  int byte_pos;  // current byte position
+  int Mapping{};
+  unsigned char UV_selector{};
+  unsigned char advance_selector{};
+  int byte_pos{};  // current byte position
 
   // quantization tables, no more than 4 quantization tables
-  std::array<std::array<long, 64>, 4> QT;
+  std::array<std::array<long, 64>, 4> QT{};
 
   // DC huffman tables , no more than 4 (0..3)
-  std::array<Huffman_table, 4> HTDC;
+  std::array<Huffman_table, 4> HTDC{};
   // AC huffman tables (0..3)
-  std::array<Huffman_table, 4> HTAC;
-  std::array<int, 256> m_CrToR;
-  std::array<int, 256> m_CbToB;
-  std::array<int, 256> m_CrToG;
-  std::array<int, 256> m_CbToG;
-  std::array<int, 256> m_Y;
-  unsigned long buffer_index;
-  uint32_t codebuf, newbuf, readbuf;
-  const unsigned char *std_luminance_qt;
-  const uint8_t *std_chrominance_qt;
+  std::array<Huffman_table, 4> HTAC{};
+  std::array<int, 256> m_CrToR{};
+  std::array<int, 256> m_CbToB{};
+  std::array<int, 256> m_CrToG{};
+  std::array<int, 256> m_CbToG{};
+  std::array<int, 256> m_Y{};
+  unsigned long buffer_index{};
+  uint32_t codebuf{}, newbuf{}, readbuf{};
+  const unsigned char *std_luminance_qt{};
+  const uint8_t *std_chrominance_qt{};
 
-  signed short int DCY, DCCb, DCCr;  // Coeficientii DC pentru Y,Cb,Cr
-  signed short int DCT_coeff[384];
+  signed short int DCY{}, DCCb{}, DCCr{};  // Coeficientii DC pentru Y,Cb,Cr
+  signed short int DCT_coeff[384]{};
   // std::vector<signed short int> DCT_coeff;  // Current DCT_coefficients
   // quantization table number for Y, Cb, Cr
   uint8_t YQ_nr = 0, CbQ_nr = 1, CrQ_nr = 1;
@@ -1321,13 +1343,13 @@
   uint8_t YAC_nr = 0, CbAC_nr = 1, CrAC_nr = 1;
   int txb = 0;
   int tyb = 0;
-  int newbits;
-  uint8_t *rlimit_table;
+  int newbits{};
+  uint8_t *rlimit_table{};
   std::vector<RGB> YUVBuffer;
   // TODO(ed) this shouldn't exist.  It is cruft that needs cleaning up
-  uint32_t *Buffer;
+  uint32_t *Buffer{};
 
  public:
   std::vector<RGB> OutBuffer;
 };
-}
\ No newline at end of file
+}  // namespace AstVideo
\ No newline at end of file
diff --git a/include/ast_video_puller.hpp b/include/ast_video_puller.hpp
index 6575d7e..759aaeb 100644
--- a/include/ast_video_puller.hpp
+++ b/include/ast_video_puller.hpp
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <assert.h>
+#include <cassert>
 #include <ast_video_types.hpp>
 #include <iostream>
 #include <mutex>
@@ -12,29 +12,29 @@
 //
 // Cursor struct is used in User Mode
 //
-typedef struct _cursor_attribution_tag {
+struct AST_CUR_ATTRIBUTION_TAG {
   unsigned int posX;
   unsigned int posY;
   unsigned int cur_width;
   unsigned int cur_height;
   unsigned int cur_type;  // 0:mono 1:color 2:disappear cursor
   unsigned int cur_change_flag;
-} AST_CUR_ATTRIBUTION_TAG;
+};
 
 //
 // For storing Cursor Information
 //
-typedef struct _cursor_tag {
+struct AST_CURSOR_TAG {
   AST_CUR_ATTRIBUTION_TAG attr;
   // unsigned char     icon[MAX_CUR_OFFSETX*MAX_CUR_OFFSETY*2];
   unsigned char *icon;  //[64*64*2];
-} AST_CURSOR_TAG;
+};
 
 //
 // For select image format, i.e. 422 JPG420, 444 JPG444, lumin/chrom table, 0
 // ~ 11, low to high
 //
-typedef struct _video_features {
+struct FEATURES_TAG {
   short jpg_fmt;  // 422:JPG420, 444:JPG444
   short lumin_tbl;
   short chrom_tbl;
@@ -42,12 +42,12 @@
   int w;
   int h;
   unsigned char *buf;
-} FEATURES_TAG;
+};
 
 //
 // For configure video engine control registers
 //
-typedef struct _image_info {
+struct IMAGE_INFO {
   short do_image_refresh;  // Action 0:motion 1:fullframe 2:quick cursor
   char qc_valid;           // quick cursor enable/disable
   unsigned int len;
@@ -57,7 +57,7 @@
     FEATURES_TAG features;
     AST_CURSOR_TAG cursor_info;
   } parameter;
-} IMAGE_INFO;
+};
 
 class SimpleVideoPuller {
  public:
@@ -66,7 +66,7 @@
   void initialize() {
     std::cout << "Opening /dev/video\n";
     video_fd = open("/dev/video", O_RDWR);
-    if (!video_fd) {
+    if (video_fd == 0) {
       std::cout << "Failed to open /dev/video\n";
       throw std::runtime_error("Failed to open /dev/video");
     }
@@ -117,16 +117,16 @@
   }
 
  private:
-  int video_fd;
+  int video_fd{};
   IMAGE_INFO image_info;
 };
 
 #if defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
 class AsyncVideoPuller {
  public:
-  typedef std::function<void(RawVideoBuffer &)> video_callback;
+  using video_callback = std::function<void (RawVideoBuffer &)>;
 
-  AsyncVideoPuller(boost::asio::io_service &io_service)
+  explicit AsyncVideoPuller(boost::asio::io_service &io_service)
       : image_info(), dev_video(io_service, open("/dev/video", O_RDWR)) {
     videobuf = std::make_shared<RawVideoBuffer>();
 
@@ -148,7 +148,7 @@
     boost::asio::async_read(
         dev_video, mutable_buffer, [this](const boost::system::error_code &ec,
                                           std::size_t bytes_transferred) {
-          if (ec) {
+          if (ec != nullptr) {
             std::cerr << "Read failed with status " << ec << "\n";
           } else {
             this->read_done();
@@ -182,4 +182,4 @@
   std::vector<video_callback> callbacks;
 };
 #endif  // defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
-}
+} // namespace AstVideo
diff --git a/include/ast_video_types.hpp b/include/ast_video_types.hpp
index 206a7ef..f5cfffd 100644
--- a/include/ast_video_types.hpp
+++ b/include/ast_video_types.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <cstdint>
 #include <vector>
 namespace AstVideo {
 enum class YuvMode { YUV444 = 0, YUV420 = 1 };
@@ -7,12 +8,12 @@
 class RawVideoBuffer {
  public:
   RawVideoBuffer() : buffer(1024 * 1024 * 10, 0){};
-  unsigned long height;
-  unsigned long width;
-  int y_selector;
-  int uv_selector;
+  unsigned long height{};
+  unsigned long width{};
+  int y_selector{};
+  int uv_selector{};
   YuvMode mode;
   // TODO(ed) determine a more appropriate buffer size
   std::vector<uint32_t> buffer;
 };
-}
\ No newline at end of file
+} // namespace AstVideo
\ No newline at end of file
diff --git a/include/base64.hpp b/include/base64.hpp
index a40ab47..092189d 100644
--- a/include/base64.hpp
+++ b/include/base64.hpp
@@ -4,4 +4,4 @@
 
 bool base64_encode(const std::string &input, std::string &output);
 bool base64_decode(const std::string &input, std::string &output);
-}
\ No newline at end of file
+} // namespace base64
\ No newline at end of file
diff --git a/include/dbus_monitor.hpp b/include/dbus_monitor.hpp
new file mode 100644
index 0000000..0306183
--- /dev/null
+++ b/include/dbus_monitor.hpp
@@ -0,0 +1,82 @@
+#pragma once
+#include <dbus/filter.hpp>
+#include <dbus/match.hpp>
+#include <dbus_singleton.hpp>
+#include <crow/app.h>
+#include <boost/container/flat_map.hpp>
+
+namespace crow {
+namespace dbus_monitor {
+
+struct DbusWebsocketSession {
+  std::vector<std::unique_ptr<dbus::match>> matches;
+  std::vector<dbus::filter> filters;
+};
+
+static boost::container::flat_map<crow::websocket::connection*,
+                                  DbusWebsocketSession>
+    sessions;
+
+void on_property_update(dbus::filter& filter, boost::system::error_code ec,
+                        dbus::message s) {
+  if (!ec) {
+    std::string object_name;
+    std::vector<std::pair<std::string, dbus::dbus_variant>> values;
+    s.unpack(object_name, values);
+    nlohmann::json j;
+    for (auto& value : values) {
+      boost::apply_visitor([&](auto val) { j[s.get_path()] = val; },
+                           value.second);
+    }
+    auto data_to_send = j.dump();
+
+    for (auto& session : sessions) {
+      session.first->send_text(data_to_send);
+    }
+  }
+  filter.async_dispatch([&](boost::system::error_code ec, dbus::message s) {
+    on_property_update(filter, ec, s);
+  });
+};
+
+template <typename... Middlewares>
+void request_routes(Crow<Middlewares...>& app) {
+  CROW_ROUTE(app, "/dbus_monitor")
+      .websocket()
+      .onopen([&](crow::websocket::connection& conn) {
+        std::string path_namespace(conn.req.url_params.get("path_namespace"));
+        if (path_namespace.empty()) {
+          conn.send_text(
+              nlohmann::json({"error", "Did not specify path_namespace"}));
+          conn.close("error");
+        }
+        sessions[&conn] = DbusWebsocketSession();
+        std::string match_string(
+            "type='signal',"
+            "interface='org.freedesktop.DBus.Properties',"
+            "path_namespace='" +
+            path_namespace + "'");
+        sessions[&conn].matches.push_back(std::make_unique<dbus::match>(
+            crow::connections::system_bus, std::move(match_string)));
+
+        sessions[&conn].filters.emplace_back(
+            crow::connections::system_bus, [path_namespace](dbus::message m) {
+              return m.get_member() == "PropertiesChanged" &&
+                     boost::starts_with(m.get_path(), path_namespace);
+            });
+        auto& this_filter = sessions[&conn].filters.back();
+        this_filter.async_dispatch(
+            [&](boost::system::error_code ec, dbus::message s) {
+              on_property_update(this_filter, ec, s);
+            });
+
+      })
+      .onclose([&](crow::websocket::connection& conn,
+                   const std::string& reason) { sessions.erase(&conn); })
+      .onmessage([&](crow::websocket::connection& conn, const std::string& data,
+                     bool is_binary) {
+        CROW_LOG_ERROR << "Got unexpected message from client on sensorws";
+      });
+}
+}  // namespace redfish
+}  // namespace crow
diff --git a/include/dbus_singleton.hpp b/include/dbus_singleton.hpp
new file mode 100644
index 0000000..e2fd2d6
--- /dev/null
+++ b/include/dbus_singleton.hpp
@@ -0,0 +1,10 @@
+#pragma once
+#include <dbus/connection.hpp>
+
+namespace crow {
+namespace connections {
+
+static std::shared_ptr<dbus::connection> system_bus;
+
+}  // namespace dbus
+}  // namespace crow
\ No newline at end of file
diff --git a/include/gzip_helper.hpp b/include/gzip_helper.hpp
new file mode 100644
index 0000000..9b7d253
--- /dev/null
+++ b/include/gzip_helper.hpp
@@ -0,0 +1,56 @@
+#pragma once
+
+#include <zlib.h>
+#include <cstring>
+#include <string>
+
+inline bool gzip_inflate(const std::string& compressedBytes,
+                        std::string& uncompressedBytes) {
+  if (compressedBytes.empty()) {
+    uncompressedBytes = compressedBytes;
+    return true;
+  }
+
+  uncompressedBytes.clear();
+
+  unsigned half_length = compressedBytes.size() / 2;
+
+  z_stream strm{};
+
+  // The following line is nolint because we're declaring away constness.
+  // It's not clear why the input buffers on zlib aren't const, so this is a
+  // bit of a cheat for the moment
+  strm.next_in = (Bytef*)compressedBytes.data();  // NOLINT
+  strm.avail_in = compressedBytes.size();
+  strm.total_out = 0;
+  strm.zalloc = Z_NULL;
+  strm.zfree = Z_NULL;
+
+  bool done = false;
+
+  if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) {
+    return false;
+  }
+
+  while (!done) {
+    // If our output buffer is too small
+    if (strm.total_out >= uncompressedBytes.size()) {
+      uncompressedBytes.resize(uncompressedBytes.size() + half_length);
+    }
+
+    strm.next_out =
+        (Bytef*)(uncompressedBytes.data() + strm.total_out);  // NOLINT
+    strm.avail_out =
+        ((uLong)uncompressedBytes.size() - strm.total_out);  // NOLINT
+
+    // Inflate another chunk.
+    int err = inflate(&strm, Z_SYNC_FLUSH);
+    if (err == Z_STREAM_END) {
+      done = true;
+    } else if (err != Z_OK) {
+      break;
+    }
+  }
+
+  return inflateEnd(&strm) == Z_OK;
+}
\ No newline at end of file
diff --git a/include/intel_oem.hpp b/include/intel_oem.hpp
new file mode 100644
index 0000000..e821807
--- /dev/null
+++ b/include/intel_oem.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <dbus_singleton.hpp>
+#include <fstream>
+#include <crow/app.h>
+
+namespace crow {
+namespace intel_oem {
+
+template <typename... Middlewares>
+void request_routes(Crow<Middlewares...>& app) {
+  CROW_ROUTE(app, "/intel/firmwareupload")
+      .methods("POST"_method)([](const crow::request& req) {
+        std::string filepath("/tmp/fw_update_image");
+        std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
+                                        std::ofstream::trunc);
+        out << req.body;
+        out.close();
+
+        auto m = dbus::message::new_call(
+            {"xyz.openbmc_project.fwupdate1.server",
+             "/xyz/openbmc_project/fwupdate1", "xyz.openbmc_project.fwupdate1"},
+            "start");
+
+        m.pack(std::string("file://") + filepath);
+        crow::connections::system_bus->send(m);
+        nlohmann::json j;
+        j["status"] = "Upload Successful";
+        return j;
+      });
+}
+}  // namespace redfish
+}  // namespace crow
diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp
new file mode 100644
index 0000000..9348808
--- /dev/null
+++ b/include/openbmc_dbus_rest.hpp
@@ -0,0 +1,320 @@
+#include <crow/app.h>
+
+#include <tinyxml2.h>
+#include <dbus/connection.hpp>
+#include <dbus/endpoint.hpp>
+#include <dbus/filter.hpp>
+#include <dbus/match.hpp>
+#include <dbus/message.hpp>
+#include <dbus_singleton.hpp>
+
+namespace crow {
+namespace openbmc_mapper {
+std::atomic<std::size_t> outstanding_async_calls(0);
+nlohmann::json object_paths;
+
+void introspect_objects(crow::response &res, std::string process_name,
+                        std::string path) {
+  dbus::endpoint introspect_endpoint(
+      process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect");
+  outstanding_async_calls++;
+  crow::connections::system_bus->async_method_call(
+      [&, process_name{std::move(process_name)}, object_path{std::move(path)} ](
+          const boost::system::error_code ec,
+          const std::string &introspect_xml) {
+        outstanding_async_calls--;
+        if (ec) {
+          std::cerr << "Introspect call failed with error: " << ec.message()
+                    << " on process: " << process_name
+                    << " path: " << object_path << "\n";
+
+        } else {
+          object_paths.push_back({{"path", object_path}});
+
+          tinyxml2::XMLDocument doc;
+
+          doc.Parse(introspect_xml.c_str());
+          tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
+          if (pRoot == nullptr) {
+            std::cerr << "XML document failed to parse " << process_name << " "
+                      << path << "\n";
+
+          } else {
+            tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
+            while (node != nullptr) {
+              std::string child_path = node->Attribute("name");
+              std::string newpath;
+              if (object_path != "/") {
+                newpath += object_path;
+              }
+              newpath += "/" + child_path;
+              // intropect the subobjects as well
+              introspect_objects(res, process_name, newpath);
+
+              node = node->NextSiblingElement("node");
+            }
+          }
+        }
+        // if we're the last outstanding caller, finish the request
+        if (outstanding_async_calls == 0) {
+          nlohmann::json j{{"status", "ok"},
+                           {"bus_name", process_name},
+                           {"objects", object_paths}};
+
+          res.write(j.dump());
+          object_paths.clear();
+          res.end();
+        }
+      },
+      introspect_endpoint);
+}
+
+template <typename... Middlewares>
+void request_routes(Crow<Middlewares...> &app) {
+  CROW_ROUTE(app, "/bus/").methods("GET"_method)([](const crow::request &req) {
+    return nlohmann::json{{"busses", {{{"name", "system"}}}}, {"status", "ok"}};
+
+  });
+
+  CROW_ROUTE(app, "/bus/system/")
+      .methods("GET"_method)([](const crow::request &req, crow::response &res) {
+        crow::connections::system_bus->async_method_call(
+            [&](const boost::system::error_code ec,
+                std::vector<std::string> &names) {
+              std::sort(names.begin(), names.end());
+              if (ec) {
+                res.code = 500;
+              } else {
+                nlohmann::json j{{"status", "ok"}};
+                auto &objects_sub = j["objects"];
+                for (auto &name : names) {
+                  objects_sub.push_back({{"name", name}});
+                }
+
+                res.write(j.dump());
+              }
+
+              res.end();
+
+            },
+            {"org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames"});
+
+      });
+
+  CROW_ROUTE(app, "/bus/system/<str>/")
+      .methods("GET"_method)([](const crow::request &req, crow::response &res,
+                                const std::string &connection) {
+        // Can only do one call at a time (for now)
+        if (outstanding_async_calls == 0) {
+          // TODO(ed) sanitize paths
+          introspect_objects(res, connection, "/");
+        } else {
+          nlohmann::json j{{"status", "failed"}};
+          res.code = 500;
+          res.write(j.dump());
+          res.end();
+        }
+      });
+
+  CROW_ROUTE(app, "/bus/system/<str>/<path>")
+      .methods("GET"_method)([](const crow::request &req, crow::response &res,
+                                const std::string &process_name,
+                                const std::string &requested_path) {
+
+        std::vector<std::string> strs;
+        boost::split(strs, requested_path, boost::is_any_of("/"));
+        std::string object_path;
+        std::string interface_name;
+        std::string method_name;
+        auto it = strs.begin();
+        if (it == strs.end()) {
+          object_path = "/";
+        }
+        while (it != strs.end()) {
+          // Check if segment contains ".".  If it does, it must be an
+          // interface
+          if ((*it).find(".") != std::string::npos) {
+            break;
+            // THis check is neccesary as the trailing slash gets parsed as part
+            // of our <path> specifier above, which causes the normal trailing
+            // backslash redirector to fail.
+          } else if (!it->empty()) {
+            object_path += "/" + *it;
+          }
+          it++;
+        }
+        if (it != strs.end()) {
+          interface_name = *it;
+          it++;
+
+          // after interface, we might have a method name
+          if (it != strs.end()) {
+            method_name = *it;
+            it++;
+          }
+        }
+        if (it != strs.end()) {
+          // if there is more levels past the method name, something went
+          // wrong, throw an error
+          res.code = 404;
+          res.end();
+          return;
+        }
+        dbus::endpoint introspect_endpoint(
+            process_name, object_path, "org.freedesktop.DBus.Introspectable",
+            "Introspect");
+        if (interface_name.empty()) {
+          crow::connections::system_bus->async_method_call(
+              [
+                    &, process_name{std::move(process_name)},
+                    object_path{std::move(object_path)}
+              ](const boost::system::error_code ec,
+                const std::string &introspect_xml) {
+                if (ec) {
+                  std::cerr
+                      << "Introspect call failed with error: " << ec.message()
+                      << " on process: " << process_name
+                      << " path: " << object_path << "\n";
+
+                } else {
+                  tinyxml2::XMLDocument doc;
+
+                  doc.Parse(introspect_xml.c_str());
+                  tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
+                  if (pRoot == nullptr) {
+                    std::cerr << "XML document failed to parse " << process_name
+                              << " " << object_path << "\n";
+                    res.write(nlohmann::json{{"status", "XML parse error"}});
+                    res.code = 500;
+                  } else {
+                    nlohmann::json interfaces_array = nlohmann::json::array();
+                    tinyxml2::XMLElement *interface =
+                        pRoot->FirstChildElement("interface");
+
+                    while (interface != nullptr) {
+                      std::string iface_name = interface->Attribute("name");
+                      interfaces_array.push_back({{"name", iface_name}});
+
+                      interface = interface->NextSiblingElement("interface");
+                    }
+                    nlohmann::json j{{"status", "ok"},
+                                     {"bus_name", process_name},
+                                     {"interfaces", interfaces_array},
+                                     {"object_path", object_path}};
+                    res.write(j.dump());
+                  }
+                }
+                res.end();
+              },
+              introspect_endpoint);
+        } else {
+          crow::connections::system_bus->async_method_call(
+              [
+                    &, process_name{std::move(process_name)},
+                    interface_name{std::move(interface_name)},
+                    object_path{std::move(object_path)}
+              ](const boost::system::error_code ec,
+                const std::string &introspect_xml) {
+                if (ec) {
+                  std::cerr
+                      << "Introspect call failed with error: " << ec.message()
+                      << " on process: " << process_name
+                      << " path: " << object_path << "\n";
+
+                } else {
+                  tinyxml2::XMLDocument doc;
+
+                  doc.Parse(introspect_xml.c_str());
+                  tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
+                  if (pRoot == nullptr) {
+                    std::cerr << "XML document failed to parse " << process_name
+                              << " " << object_path << "\n";
+                    res.code = 500;
+
+                  } else {
+                    tinyxml2::XMLElement *node =
+                        pRoot->FirstChildElement("node");
+
+                    // if we know we're the only call, build the json directly
+                    nlohmann::json methods_array = nlohmann::json::array();
+                    nlohmann::json signals_array = nlohmann::json::array();
+                    tinyxml2::XMLElement *interface =
+                        pRoot->FirstChildElement("interface");
+
+                    while (interface != nullptr) {
+                      std::string iface_name = interface->Attribute("name");
+
+                      if (iface_name == interface_name) {
+                        tinyxml2::XMLElement *methods =
+                            interface->FirstChildElement("method");
+                        while (methods != nullptr) {
+                          nlohmann::json args_array = nlohmann::json::array();
+                          tinyxml2::XMLElement *arg =
+                              methods->FirstChildElement("arg");
+                          while (arg != nullptr) {
+                            args_array.push_back(
+                                {{"name", arg->Attribute("name")},
+                                 {"type", arg->Attribute("type")},
+                                 {"direction", arg->Attribute("direction")}});
+                            arg = arg->NextSiblingElement("arg");
+                          }
+                          methods_array.push_back(
+                              {{"name", methods->Attribute("name")},
+                               {"uri", "/bus/system/" + process_name +
+                                           object_path + "/" + interface_name +
+                                           "/" + methods->Attribute("name")},
+                               {"args", args_array}});
+                          methods = methods->NextSiblingElement("method");
+                        }
+                        tinyxml2::XMLElement *signals =
+                            interface->FirstChildElement("signal");
+                        while (signals != nullptr) {
+                          nlohmann::json args_array = nlohmann::json::array();
+
+                          tinyxml2::XMLElement *arg =
+                              signals->FirstChildElement("arg");
+                          while (arg != nullptr) {
+                            std::string name = arg->Attribute("name");
+                            std::string type = arg->Attribute("type");
+                            args_array.push_back({
+                                {"name", name}, {"type", type},
+                            });
+                            arg = arg->NextSiblingElement("arg");
+                          }
+                          signals_array.push_back(
+                              {{"name", signals->Attribute("name")},
+                               {"args", args_array}});
+                          signals = signals->NextSiblingElement("signal");
+                        }
+
+                        nlohmann::json j{
+                            {"status", "ok"},
+                            {"bus_name", process_name},
+                            {"interface", interface_name},
+                            {"methods", methods_array},
+                            {"object_path", object_path},
+                            {"properties", nlohmann::json::object()},
+                            {"signals", signals_array}};
+
+                        res.write(j.dump());
+                        break;
+                      }
+
+                      interface = interface->NextSiblingElement("interface");
+                    }
+                    if (interface == nullptr) {
+                      // if we got to the end of the list and never found a
+                      // match, throw 404
+                      res.code = 404;
+                    }
+                  }
+                }
+                res.end();
+              },
+              introspect_endpoint);
+        }
+
+      });
+}
+}  // namespace openbmc_mapper
+}  // namespace crow
diff --git a/include/pam_authenticate.hpp b/include/pam_authenticate.hpp
index 153dbc7..0825dd6 100644
--- a/include/pam_authenticate.hpp
+++ b/include/pam_authenticate.hpp
@@ -1,20 +1,28 @@
+#pragma once
+
 #include <security/pam_appl.h>
+#include <cstring>
 
 // function used to get user input
 inline int pam_function_conversation(int num_msg,
                                      const struct pam_message** msg,
                                      struct pam_response** resp,
                                      void* appdata_ptr) {
-  char* pass = (char*)malloc(strlen((char*)appdata_ptr) + 1);
-  strcpy(pass, (char*)appdata_ptr);
+  if (appdata_ptr == nullptr) {
+    return PAM_AUTH_ERR;
+  }
+  auto* pass = reinterpret_cast<char*>(
+      malloc(std::strlen(reinterpret_cast<char*>(appdata_ptr)) + 1));
+  std::strcpy(pass, reinterpret_cast<char*>(appdata_ptr));
 
-  int i;
+  *resp = reinterpret_cast<pam_response*>(
+      calloc(num_msg, sizeof(struct pam_response)));
 
-  *resp = (pam_response*)calloc(num_msg, sizeof(struct pam_response));
-
-  for (i = 0; i < num_msg; ++i) {
+  for (int i = 0; i < num_msg; ++i) {
     /* Ignore all PAM messages except prompting for hidden input */
-    if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) continue;
+    if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) {
+      continue;
+    }
 
     /* Assume PAM is only prompting for the password as hidden input */
     resp[i]->resp = pass;
@@ -23,43 +31,39 @@
   return PAM_SUCCESS;
 }
 
-class PamAuthenticator {
- public:
-  inline bool authenticate(const std::string& username,
-                           const std::string& password) {
-    const struct pam_conv local_conversation = {pam_function_conversation,
-                                                (char*)password.c_str()};
-    pam_handle_t* local_auth_handle = NULL;  // this gets set by pam_start
+inline bool pam_authenticate_user(const std::string& username,
+                                  const std::string& password) {
+  const struct pam_conv local_conversation = {
+      pam_function_conversation, const_cast<char*>(password.c_str())};
+  pam_handle_t* local_auth_handle = NULL;  // this gets set by pam_start
 
-    int retval;
-    retval = pam_start("su", username.c_str(), &local_conversation,
-                       &local_auth_handle);
-
-    if (retval != PAM_SUCCESS) {
-      //printf("pam_start returned: %d\n ", retval);
-      return false;
-    }
-
-    retval = pam_authenticate(local_auth_handle,
-                              PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
-
-    if (retval != PAM_SUCCESS) {
-      if (retval == PAM_AUTH_ERR) {
-        //printf("Authentication failure.\n");
-      } else {
-        //printf("pam_authenticate returned %d\n", retval);
-      }
-      return false;
-    }
-
-    //printf("Authenticated.\n");
-    retval = pam_end(local_auth_handle, retval);
-
-    if (retval != PAM_SUCCESS) {
-      //printf("pam_end returned\n");
-      return false;
-    }
-
-    return true;
+  if (pam_start("su", username.c_str(), &local_conversation,
+                &local_auth_handle) != PAM_SUCCESS) {
+    return false;
   }
-};
\ No newline at end of file
+  int retval = pam_authenticate(local_auth_handle,
+                                PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
+
+  if (retval != PAM_SUCCESS) {
+    if (retval == PAM_AUTH_ERR) {
+      // printf("Authentication failure.\n");
+    } else {
+      // printf("pam_authenticate returned %d\n", retval);
+    }
+    pam_end(local_auth_handle, PAM_SUCCESS);
+    return false;
+  }
+
+  /* check that the account is healthy */
+  if (pam_acct_mgmt(local_auth_handle, PAM_DISALLOW_NULL_AUTHTOK) !=
+      PAM_SUCCESS) {
+    pam_end(local_auth_handle, PAM_SUCCESS);
+    return false;
+  }
+
+  if (pam_end(local_auth_handle, PAM_SUCCESS) != PAM_SUCCESS) {
+    return false;
+  }
+
+  return true;
+}
diff --git a/include/redfish_v1.hpp b/include/redfish_v1.hpp
index c302a68..73ed84e 100644
--- a/include/redfish_v1.hpp
+++ b/include/redfish_v1.hpp
@@ -1,9 +1,24 @@
+#pragma once
+
 #include <crow/app.h>
+
+#include <dbus/connection.hpp>
+#include <dbus/endpoint.hpp>
+#include <dbus/filter.hpp>
+#include <dbus/match.hpp>
+#include <dbus/message.hpp>
+#include <fstream>
+
 namespace crow {
 namespace redfish {
 
 template <typename... Middlewares>
 void request_routes(Crow<Middlewares...>& app) {
+  
+  // noop for now
+  return;
+
+
   CROW_ROUTE(app, "/redfish/").methods("GET"_method)([]() {
     return nlohmann::json{{"v1", "/redfish/v1/"}};
   });
@@ -28,6 +43,28 @@
     };
   });
 
+  CROW_ROUTE(app, "/redfish/v1/Chassis").methods("GET"_method)([]() {
+    std::vector<std::string> entities;
+    std::ifstream f("~/system.json");
+    nlohmann::json input;
+    input << f;
+    for (auto it = input.begin(); it != input.end(); it++) {
+      auto value = it.value();
+      if (value["type"] == "Chassis") {
+         std::string str = value["name"];
+         entities.emplace_back(str);
+      }
+    }
+    auto ret = nlohmann::json{
+        {"@odata.context",
+         "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"},
+        {"@odata.id", "/redfish/v1/Chassis"},
+        {"@odata.type", "#ChassisCollection.ChassisCollection"},
+        {"Name", "Chassis Collection"},
+        {"Members@odata.count", entities.size()}};
+    return ret;
+  });
+
   CROW_ROUTE(app, "/redfish/v1/AccountService").methods("GET"_method)([]() {
     return nlohmann::json{
         {"@odata.context",
@@ -38,6 +75,7 @@
         {"Name", "Account Service"},
         {"Description", "BMC User Accounts"},
         {"Status",
+         // TODO(ed) health rollup
          {{"State", "Enabled"}, {"Health", "OK"}, {"HealthRollup", "OK"}}},
         {"ServiceEnabled", true},
         {"MinPasswordLength", 1},
@@ -47,22 +85,41 @@
     };
   });
 
-  CROW_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
-      .methods("GET"_method)([]() {
-        return nlohmann::json{
-            {"@odata.context",
-             "/redfish/v1/"
-             "$metadata#ManagerAccountCollection.ManagerAccountCollection"},
-            {"@odata.id", "/redfish/v1/AccountService/Accounts"},
-            {"@odata.type",
-             "#ManagerAccountCollection.ManagerAccountCollection"},
-            {"Name", "Accounts Collection"},
-            {"Description", "BMC User Accounts"},
-            {"Members@odata.count", 3},
-            {"Members",
-             {{{"@odata.id", "/redfish/v1/AccountService/Accounts/1"}},
-              {{"@odata.id", "/redfish/v1/AccountService/Accounts/2"}},
-              {{"@odata.id", "/redfish/v1/AccountService/Accounts/3"}}}}};
+  CROW_ROUTE(app, "/redfish/v1/AccountService/Accounts")
+      .methods("GET"_method)([](const crow::request& req, crow::response& res) {
+        boost::asio::io_service io;
+        auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
+        dbus::endpoint user_list("org.openbmc.UserManager",
+                                 "/org/openbmc/UserManager/Users",
+                                 "org.openbmc.Enrol", "UserList");
+        bus->async_method_call(
+            [&](const boost::system::error_code ec,
+                           const std::vector<std::string>& users) {
+              if (ec) {
+                res.code = 500;
+              } else {
+                nlohmann::json return_json{
+                    {"@odata.context",
+                     "/redfish/v1/"
+                     "$metadata#ManagerAccountCollection."
+                     "ManagerAccountCollection"},
+                    {"@odata.id", "/redfish/v1/AccountService/Accounts"},
+                    {"@odata.type",
+                     "#ManagerAccountCollection.ManagerAccountCollection"},
+                    {"Name", "Accounts Collection"},
+                    {"Description", "BMC User Accounts"},
+                    {"Members@odata.count", users.size()}};
+                nlohmann::json member_array;
+                int user_index = 0;
+                for (auto& user : users) {
+                  member_array.push_back(
+                      {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
+                                         std::to_string(++user_index)}});
+                }
+                return_json["Members"] = member_array;
+              }
+              res.end();
+            }, user_list);
       });
 
   CROW_ROUTE(app, "/redfish/v1/AccountService/Accounts/<int>/")
@@ -84,5 +141,5 @@
                {{"@odata.id", "/redfish/v1/AccountService/Roles/NoAccess"}}}}}};
       });
 }
-}
-}
\ No newline at end of file
+}  // namespace redfish
+}  // namespace crow
diff --git a/include/security_headers_middleware.hpp b/include/security_headers_middleware.hpp
index 19644f4..e12395a 100644
--- a/include/security_headers_middleware.hpp
+++ b/include/security_headers_middleware.hpp
@@ -4,34 +4,28 @@
 #include <crow/http_response.h>
 
 namespace crow {
-static const std::string strict_transport_security_key =
-    "Strict-Transport-Security";
-static const std::string strict_transport_security_value =
+static const char* strict_transport_security_key = "Strict-Transport-Security";
+static const char* strict_transport_security_value =
     "max-age=31536000; includeSubdomains; preload";
 
-static const std::string ua_compatability_key = "X-UA-Compatible";
-static const std::string ua_compatability_value = "IE=11";
+static const char* ua_compatability_key = "X-UA-Compatible";
+static const char* ua_compatability_value = "IE=11";
 
-static const std::string xframe_key = "X-Frame-Options";
-static const std::string xframe_value = "DENY";
+static const char* xframe_key = "X-Frame-Options";
+static const char* xframe_value = "DENY";
 
-static const std::string xss_key = "X-XSS-Protection";
-static const std::string xss_value = "1; mode=block";
+static const char* xss_key = "X-XSS-Protection";
+static const char* xss_value = "1; mode=block";
 
-static const std::string content_security_key = "X-Content-Security-Policy";
-static const std::string content_security_value = "default-src 'self'";
-
+static const char* content_security_key = "X-Content-Security-Policy";
+static const char* content_security_value = "default-src 'self'";
 
 struct SecurityHeadersMiddleware {
   struct context {};
 
-  void before_handle(crow::request& req,
-                                                       response& res,
-                                                       context& ctx) {}
+  void before_handle(crow::request& req, response& res, context& ctx) {}
 
-  void after_handle(request& /*req*/,
-                                                      response& res,
-                                                      context& ctx) {
+  void after_handle(request& req, response& res, context& ctx) {
     /*
      TODO(ed) these should really check content types.  for example,
      X-UA-Compatible header doesn't make sense when retrieving a JSON or
@@ -43,6 +37,8 @@
     res.add_header(xframe_key, xframe_value);
     res.add_header(xss_key, xss_value);
     res.add_header(content_security_key, content_security_value);
+    res.add_header("Access-Control-Allow-Origin", "http://localhost:8085");
+    res.add_header("Access-Control-Allow-Credentials", "true");
   }
 };
-}
\ No newline at end of file
+}  // namespace crow
diff --git a/include/ssl_key_handler.hpp b/include/ssl_key_handler.hpp
index 4948025..2a1a852 100644
--- a/include/ssl_key_handler.hpp
+++ b/include/ssl_key_handler.hpp
@@ -15,11 +15,11 @@
 #include <boost/asio.hpp>
 
 namespace ensuressl {
-static void init_openssl(void);
-static void cleanup_openssl(void);
-static EVP_PKEY *create_rsa_key(void);
-static EVP_PKEY *create_ec_key(void);
-static void handle_openssl_error(void);
+static void init_openssl();
+static void cleanup_openssl();
+static EVP_PKEY *create_rsa_key();
+static EVP_PKEY *create_ec_key();
+static void handle_openssl_error();
 
 inline bool verify_openssl_key_cert(const std::string &filepath) {
   bool private_key_valid = false;
@@ -31,9 +31,9 @@
   if (file != NULL) {
     EVP_PKEY *pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL);
     int rc;
-    if (pkey) {
+    if (pkey != nullptr) {
       RSA *rsa = EVP_PKEY_get1_RSA(pkey);
-      if (rsa) {
+      if (rsa != nullptr) {
         std::cout << "Found an RSA key\n";
         if (RSA_check_key(rsa) == 1) {
           // private_key_valid = true;
@@ -43,12 +43,13 @@
         RSA_free(rsa);
       } else {
         EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey);
-        if (ec) {
+        if (ec != nullptr) {
           std::cout << "Found an EC key\n";
           if (EC_KEY_check_key(ec) == 1) {
             private_key_valid = true;
           } else {
-            std::cerr << "Key not valid error number " << ERR_get_error() << "\n";
+            std::cerr << "Key not valid error number " << ERR_get_error()
+                      << "\n";
           }
           EC_KEY_free(ec);
         }
@@ -56,7 +57,7 @@
 
       if (private_key_valid) {
         X509 *x509 = PEM_read_X509(file, NULL, NULL, NULL);
-        if (!x509) {
+        if (x509 == nullptr) {
           std::cout << "error getting x509 cert " << ERR_get_error() << "\n";
         } else {
           rc = X509_verify(x509, pkey);
@@ -64,7 +65,7 @@
             cert_valid = true;
           } else {
             std::cerr << "Error in verifying private key signature "
-                         << ERR_get_error() << "\n";
+                      << ERR_get_error() << "\n";
           }
         }
       }
@@ -86,12 +87,12 @@
 
   std::cerr << "Generating EC key\n";
   EVP_PKEY *pRsaPrivKey = create_ec_key();
-  if (pRsaPrivKey) {
+  if (pRsaPrivKey != nullptr) {
     std::cerr << "Generating x509 Certificate\n";
     // Use this code to directly generate a certificate
     X509 *x509;
     x509 = X509_new();
-    if (x509) {
+    if (x509 != nullptr) {
       // Get a random number from the RNG for the certificate serial number
       // If this is not random, regenerating certs throws broswer errors
       std::random_device rd;
@@ -111,12 +112,15 @@
       X509_NAME *name;
       name = X509_get_subject_name(x509);
 
-      X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)"US",
+      X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+                                 reinterpret_cast<const unsigned char *>("US"),
                                  -1, -1, 0);
-      X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
-                                 (unsigned char *)"Intel BMC", -1, -1, 0);
-      X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
-                                 (unsigned char *)"testhost", -1, -1, 0);
+      X509_NAME_add_entry_by_txt(
+          name, "O", MBSTRING_ASC,
+          reinterpret_cast<const unsigned char *>("Intel BMC"), -1, -1, 0);
+      X509_NAME_add_entry_by_txt(
+          name, "CN", MBSTRING_ASC,
+          reinterpret_cast<const unsigned char *>("testhost"), -1, -1, 0);
       // set the CSR options
       X509_set_issuer_name(x509, name);
 
@@ -125,7 +129,7 @@
 
       pFile = fopen(filepath.c_str(), "wt");
 
-      if (pFile) {
+      if (pFile != nullptr) {
         PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0, NULL);
 
         PEM_write_X509(pFile, x509);
@@ -143,7 +147,7 @@
   // cleanup_openssl();
 }
 
-EVP_PKEY *create_rsa_key(void) {
+EVP_PKEY *create_rsa_key() {
   RSA *pRSA = NULL;
 #if OPENSSL_VERSION_NUMBER < 0x00908000L
   pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL);
@@ -152,7 +156,8 @@
 #endif
 
   EVP_PKEY *pKey = EVP_PKEY_new();
-  if (pRSA && pKey && EVP_PKEY_assign_RSA(pKey, pRSA)) {
+  if ((pRSA != nullptr) && (pKey != nullptr) &&
+      EVP_PKEY_assign_RSA(pKey, pRSA)) {
     /* pKey owns pRSA from now */
     if (RSA_check_key(pRSA) <= 0) {
       fprintf(stderr, "RSA_check_key failed.\n");
@@ -162,11 +167,11 @@
     }
   } else {
     handle_openssl_error();
-    if (pRSA) {
+    if (pRSA != nullptr) {
       RSA_free(pRSA);
       pRSA = NULL;
     }
-    if (pKey) {
+    if (pKey != nullptr) {
       EVP_PKEY_free(pKey);
       pKey = NULL;
     }
@@ -174,17 +179,17 @@
   return pKey;
 }
 
-EVP_PKEY *create_ec_key(void) {
+EVP_PKEY *create_ec_key() {
   EVP_PKEY *pKey = NULL;
   int eccgrp = 0;
   eccgrp = OBJ_txt2nid("prime256v1");
 
   EC_KEY *myecc = EC_KEY_new_by_curve_name(eccgrp);
-  if (myecc) {
+  if (myecc != nullptr) {
     EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE);
     EC_KEY_generate_key(myecc);
     pKey = EVP_PKEY_new();
-    if (pKey) {
+    if (pKey != nullptr) {
       if (EVP_PKEY_assign_EC_KEY(pKey, myecc)) {
         /* pKey owns pRSA from now */
         if (EC_KEY_check_key(myecc) <= 0) {
@@ -196,15 +201,15 @@
   return pKey;
 }
 
-void init_openssl(void) {
- #if OPENSSL_VERSION_NUMBER < 0x10100000L
-    SSL_load_error_strings();
-    OpenSSL_add_all_algorithms();
-    RAND_load_file("/dev/urandom", 1024);
+void init_openssl() {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+  SSL_load_error_strings();
+  OpenSSL_add_all_algorithms();
+  RAND_load_file("/dev/urandom", 1024);
 #endif
 }
 
-void cleanup_openssl(void) {
+void cleanup_openssl() {
   CRYPTO_cleanup_all_ex_data();
   ERR_free_strings();
 #if OPENSSL_VERSION_NUMBER < 0x10100000L
@@ -213,7 +218,7 @@
   EVP_cleanup();
 }
 
-void handle_openssl_error(void) { ERR_print_errors_fp(stderr); }
+void handle_openssl_error() { ERR_print_errors_fp(stderr); }
 inline void ensure_openssl_key_present_and_valid(const std::string &filepath) {
   bool pem_file_valid = false;
 
@@ -225,7 +230,8 @@
   }
 }
 
-boost::asio::ssl::context get_ssl_context(std::string ssl_pem_file) {
+inline boost::asio::ssl::context get_ssl_context(
+    const std::string &ssl_pem_file) {
   boost::asio::ssl::context m_ssl_context{boost::asio::ssl::context::sslv23};
   m_ssl_context.set_options(boost::asio::ssl::context::default_workarounds |
                             boost::asio::ssl::context::no_sslv2 |
@@ -249,7 +255,7 @@
   }
 
   // From mozilla "compatibility"
-  std::string ciphers =
+  std::string mozilla_compatibility_ciphers =
       "ECDHE-ECDSA-CHACHA20-POLY1305:"
       "ECDHE-RSA-CHACHA20-POLY1305:"
       "ECDHE-ECDSA-AES128-GCM-SHA256:"
@@ -283,7 +289,7 @@
       "!DSS";
 
   // From mozilla "modern"
-  std::string modern_ciphers =
+  std::string mozilla_modern_ciphers =
       "ECDHE-ECDSA-AES256-GCM-SHA384:"
       "ECDHE-RSA-AES256-GCM-SHA384:"
       "ECDHE-ECDSA-CHACHA20-POLY1305:"
@@ -295,14 +301,14 @@
       "ECDHE-ECDSA-AES128-SHA256:"
       "ECDHE-RSA-AES128-SHA256";
 
-  std::string lighttp_ciphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL";
+  std::string aes_only_ciphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL";
 
-  if (SSL_CTX_set_cipher_list(m_ssl_context.native_handle(), lighttp_ciphers.c_str()) !=
-      1) {
+  if (SSL_CTX_set_cipher_list(m_ssl_context.native_handle(),
+                              mozilla_compatibility_ciphers.c_str()) != 1) {
     CROW_LOG_ERROR << "Error setting cipher list\n";
   }
   return m_ssl_context;
 }
-}
+}  // namespace ensuressl
 
 #endif
\ No newline at end of file
diff --git a/include/test_utils.hpp b/include/test_utils.hpp
deleted file mode 100644
index eb990d5..0000000
--- a/include/test_utils.hpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-
-#include <string>
-
-bool gzipInflate(const std::string& compressedBytes,
-                 std::string& uncompressedBytes);
\ No newline at end of file
diff --git a/include/token_authorization_middleware.hpp b/include/token_authorization_middleware.hpp
index 2d1edcd..eebd4f0 100644
--- a/include/token_authorization_middleware.hpp
+++ b/include/token_authorization_middleware.hpp
@@ -1,32 +1,32 @@
 #pragma once
 
+#include <base64.hpp>
+#include <pam_authenticate.hpp>
+#include <webassets.hpp>
+#include <random>
+#include <crow/app.h>
 #include <crow/http_request.h>
 #include <crow/http_response.h>
 #include <boost/container/flat_set.hpp>
 
-#include <base64.hpp>
-
-#include <pam_authenticate.hpp>
-
 namespace crow {
 
+namespace TokenAuthorization {
 struct User {};
 
 using random_bytes_engine =
-    std::independent_bits_engine<std::default_random_engine, CHAR_BIT,
-                                 unsigned char>;
+    std::independent_bits_engine<std::random_device, CHAR_BIT, unsigned char>;
 
-template <class AuthenticationFunction>
-struct TokenAuthorization {
- private:
-  random_bytes_engine rbe;
-
+class Middleware {
  public:
-  struct context {
-    // std::string auth_token;
+  Middleware() {
+    for (auto& route : crow::webassets::routes) {
+      allowed_routes.emplace(route);
+    }
+    allowed_routes.emplace("/login");
   };
 
-  TokenAuthorization(){};
+  struct context {};
 
   void before_handle(crow::request& req, response& res, context& ctx) {
     auto return_unauthorized = [&req, &res]() {
@@ -34,100 +34,64 @@
       res.end();
     };
 
-    auto return_bad_request = [&req, &res]() {
-      res.code = 400;
-      res.end();
-    };
-
-    auto return_internal_error = [&req, &res]() {
-      res.code = 500;
-      res.end();
-    };
-
-    if (req.url == "/" || boost::starts_with(req.url, "/static/")) {
+    if (allowed_routes.find(req.url.c_str()) != allowed_routes.end()) {
       // TODO this is total hackery to allow the login page to work before the
       // user is authenticated.  Also, it will be quite slow for all pages
       // instead of a one time hit for the whitelist entries.  Ideally, this
-      // should be
-      // done in the url router handler, with tagged routes for the whitelist
-      // entries. Another option would be to whitelist a minimal for based page
-      // that didn't load the full angular UI until after login
-      return;
-    }
-
-    if (req.url == "/login") {
-      if (req.method != HTTPMethod::POST) {
-        return_unauthorized();
-        return;
-      } else {
-        std::string username;
-        std::string password;
-        try {
-          auto login_credentials = nlohmann::json::parse(req.body);
-          username = login_credentials["username"];
-          password = login_credentials["password"];
-        } catch (...) {
-          return_bad_request();
-          return;
-        }
-
-        auto p = AuthenticationFunction();
-        if (p.authenticate(username, password)) {
-          nlohmann::json x;
-
-          std::string token('a', 20);
-          // TODO(ed) for some reason clang-tidy finds a divide by zero error in
-          // cstdlibc here commented out for now.  Needs investigation
-          std::generate(std::begin(token), std::end(token),
-                        std::ref(rbe));  // NOLINT
-          std::string encoded_token;
-          base64::base64_encode(token, encoded_token);
-          // ctx.auth_token = encoded_token;
-          this->auth_token2.insert(encoded_token);
-
-          nlohmann::json ret{{"token", encoded_token}};
-
-          res.write(ret.dump());
-          res.add_header("Content-Type", "application/json");
-          res.end();
-        } else {
+      // should be done in the url router handler, with tagged routes for the
+      // whitelist entries. Another option would be to whitelist a minimal form
+      // based page that didn't load the full angular UI until after login
+    } else {
+      // Normal, non login, non static file request
+      // Check for an authorization header, reject if not present
+      std::string auth_key;
+      if (req.headers.count("Authorization") == 1) {
+        std::string auth_header = req.get_header_value("Authorization");
+        // If the user is attempting any kind of auth other than token, reject
+        if (!boost::starts_with(auth_header, "Token ")) {
           return_unauthorized();
           return;
         }
+        auth_key = auth_header.substr(6);
+      } else {
+        int count = req.headers.count("Cookie");
+        if (count == 1) {
+          auto& cookie_value = req.get_header_value("Cookie");
+          auto start_index = cookie_value.find("SESSION=");
+          if (start_index != std::string::npos) {
+            start_index += 8;
+            auto end_index = cookie_value.find(";", start_index);
+            if (end_index == std::string::npos) {
+              end_index = cookie_value.size() - 1;
+            }
+            auth_key =
+                cookie_value.substr(start_index, end_index - start_index + 1);
+          }
+        }
+      }
+      if (auth_key.empty()) {
+        res.code = 400;
+        res.end();
+        return;
+      }
+      std::cout << "auth_key=" << auth_key << "\n";
+
+      for (auto& token : this->auth_tokens) {
+        std::cout << "token=" << token << "\n";
       }
 
-    } else {  // Normal, non login, non static file request
-      // Check to make sure we're logged in
-      if (this->auth_token2.empty()) {
-        return_unauthorized();
-        return;
-      }
-      // Check for an authorization header, reject if not present
-      if (req.headers.count("Authorization") != 1) {
-        return_unauthorized();
-        return;
-      }
-
-      std::string auth_header = req.get_header_value("Authorization");
-      // If the user is attempting any kind of auth other than token, reject
-      if (!boost::starts_with(auth_header, "Token ")) {
-        return_unauthorized();
-        return;
-      }
-      std::string auth_key = auth_header.substr(6);
       // TODO(ed), use span here instead of constructing a new string
-      if (this->auth_token2.find(auth_key) == this->auth_token2.end()) {
+      if (this->auth_tokens.find(auth_key) == this->auth_tokens.end()) {
         return_unauthorized();
         return;
       }
 
       if (req.url == "/logout") {
-        this->auth_token2.erase(auth_key);
+        this->auth_tokens.erase(auth_key);
         res.code = 200;
         res.end();
         return;
       }
-
       // else let the request continue unharmed
     }
   }
@@ -136,9 +100,128 @@
     // Do nothing
   }
 
- private:
-  boost::container::flat_set<std::string> auth_token2;
+  boost::container::flat_set<std::string> auth_tokens;
+  boost::container::flat_set<std::string> allowed_routes;
+  random_bytes_engine rbe;
 };
 
-using TokenAuthorizationMiddleware = TokenAuthorization<PamAuthenticator>;
-}
\ No newline at end of file
+// TODO(ed) see if there is a better way to allow middlewares to request routes.
+// Possibly an init function on first construction?
+template <typename... Middlewares>
+void request_routes(Crow<Middlewares...>& app) {
+  static_assert(black_magic::contains<TokenAuthorization::Middleware,
+                                      Middlewares...>::value,
+                "TokenAuthorization middleware must be enabled in app to use "
+                "auth routes");
+  CROW_ROUTE(app, "/login")
+      .methods(
+          "POST"_method)([&](const crow::request& req, crow::response& res) {
+        std::string content_type;
+        auto content_type_it = req.headers.find("content-type");
+        if (content_type_it != req.headers.end()) {
+          content_type = content_type_it->second;
+          boost::algorithm::to_lower(content_type);
+        }
+        std::string username;
+        std::string password;
+        bool looks_like_ibm = false;
+        // Check if auth was provided by a payload
+        if (content_type == "application/json") {
+          try {
+            auto login_credentials = nlohmann::json::parse(req.body);
+            // check for username/password in the root object
+            // THis method is how intel APIs authenticate
+            auto user_it = login_credentials.find("username");
+            auto pass_it = login_credentials.find("password");
+            if (user_it != login_credentials.end() &&
+                pass_it != login_credentials.end()) {
+              username = user_it->get<const std::string>();
+              password = pass_it->get<const std::string>();
+            } else {
+              // Openbmc appears to push a data object that contains the same
+              // keys (username and password), attempt to use that
+              auto data_it = login_credentials.find("data");
+              if (data_it != login_credentials.end()) {
+                // Some apis produce an array of value ["username", "password"]
+                if (data_it->is_array()) {
+                  if (data_it->size() == 2) {
+                    username = (*data_it)[0].get<const std::string>();
+                    password = (*data_it)[1].get<const std::string>();
+                    looks_like_ibm = true;
+                  }
+                } else if (data_it->is_object()) {
+                  auto user_it = data_it->find("username");
+                  auto pass_it = data_it->find("password");
+                  if (user_it != data_it->end() && pass_it != data_it->end()) {
+                    username = user_it->get<const std::string>();
+                    password = pass_it->get<const std::string>();
+                  }
+                }
+              }
+            }
+          } catch (...) {
+            // TODO(ed) figure out how to not throw on a bad json parse
+            res.code = 400;
+            res.end();
+            return;
+          }
+        } else {
+          // check if auth was provided as a query string
+          auto user_it = req.headers.find("username");
+          auto pass_it = req.headers.find("password");
+          if (user_it != req.headers.end() && pass_it != req.headers.end()) {
+            username = user_it->second;
+            password = pass_it->second;
+          }
+        }
+
+        if (!username.empty() && !password.empty()) {
+          if (!pam_authenticate_user(username, password)) {
+            res.code = 401;
+          } else {
+            // THis should be a multiple of 3 to make sure that base64 doesn't
+            // end with an equals sign at the end.  we could strip it off
+            // afterward
+            std::string token(30, 'a');
+            // TODO(ed) for some reason clang-tidy finds a divide by zero
+            // error in cstdlibc here commented out for now.
+            // Needs investigation
+            auto& m = app.template get_middleware<Middleware>();
+            std::generate(std::begin(token), std::end(token),
+                          std::ref(m.rbe));  // NOLINT
+            std::string encoded_token;
+            if (!base64::base64_encode(token, encoded_token)) {
+              res.code = 500;
+            } else {
+              
+              m.auth_tokens.insert(encoded_token);
+              if (looks_like_ibm) {
+                // IBM requires a very specific login structure, and doesn't
+                // actually look at the status code.  TODO(ed).... Fix that
+                // upstream
+                nlohmann::json ret{
+                    {"data", "User '" + username + "' logged in"},
+                    {"message", "200 OK"},
+                    {"status", "ok"}};
+                res.add_header(
+                    "Set-Cookie",
+                    "SESSION=" + encoded_token + "; Secure; HttpOnly");
+                res.write(ret.dump());
+              } else {
+                // if content type is json, assume json token
+                nlohmann::json ret{{"token", encoded_token}};
+
+                res.write(ret.dump());
+                res.add_header("Content-Type", "application/json");
+              }
+            }
+          }
+
+        } else {
+          res.code = 400;
+        }
+        res.end();
+      });
+}
+}  // namespaec TokenAuthorization
+}  // namespace crow
\ No newline at end of file
diff --git a/include/web_kvm.hpp b/include/web_kvm.hpp
index 3d33347..82cb488 100644
--- a/include/web_kvm.hpp
+++ b/include/web_kvm.hpp
@@ -119,20 +119,20 @@
 };
 
 struct framebuffer_rectangle {
-  boost::endian::big_uint16_t x;
-  boost::endian::big_uint16_t y;
-  boost::endian::big_uint16_t width;
-  boost::endian::big_uint16_t height;
-  boost::endian::big_uint32_t encoding;
+  boost::endian::big_uint16_t x{};
+  boost::endian::big_uint16_t y{};
+  boost::endian::big_uint16_t width{};
+  boost::endian::big_uint16_t height{};
+  boost::endian::big_uint32_t encoding{};
   std::vector<uint8_t> data;
 };
 
 struct framebuffer_update_msg {
-  boost::endian::big_uint8_t message_type;
+  boost::endian::big_uint8_t message_type{};
   std::vector<framebuffer_rectangle> rectangles;
 };
 
-std::string serialize(const framebuffer_update_msg& msg) {
+inline std::string serialize(const framebuffer_update_msg& msg) {
   // calculate the size of the needed vector for serialization
   size_t vector_size = 4;
   for (const auto& rect : msg.rectangles) {
@@ -174,12 +174,12 @@
 
 class connection_metadata {
  public:
-  connection_metadata(void) : vnc_state(VncState::UNSTARTED){};
+  connection_metadata() {};
 
-  VncState vnc_state;
+  VncState vnc_state{VncState::UNSTARTED};
 };
 
-typedef std::vector<connection_metadata> meta_list;
+using meta_list = std::vector<connection_metadata>;
 meta_list connection_states(10);
 
 connection_metadata meta;
@@ -235,7 +235,7 @@
           } break;
           case VncState::AWAITING_CLIENT_INIT_msg: {
             // Now send the server initialization
-            server_initialization_msg server_init_msg;
+            server_initialization_msg server_init_msg{};
             server_init_msg.framebuffer_width = 800;
             server_init_msg.framebuffer_height = 600;
             server_init_msg.pixel_format.bits_per_pixel = 32;
@@ -261,8 +261,8 @@
           case VncState::MAIN_LOOP: {
             if (data.size() >= sizeof(client_to_server_msg_type)) {
               auto type = static_cast<client_to_server_msg_type>(data[0]);
-              std::cout << "Received client message type " << (uint32_t)type
-                         << "\n";
+              std::cout << "Received client message type "
+                        << static_cast<std::size_t>(type) << "\n";
               switch (type) {
                 case client_to_server_msg_type::set_pixel_format: {
                 } break;
@@ -277,7 +277,9 @@
                   if (data.size() >= sizeof(frame_buffer_update_req) +
                                          sizeof(client_to_server_msg_type)) {
                     auto msg = reinterpret_cast<const frame_buffer_update_req*>(
-                        data.data() + sizeof(client_to_server_msg_type));
+                        data.data() +   // NOLINT
+                        sizeof(client_to_server_msg_type));
+                    // TODO(ed) find a better way to do this deserialization
 
                     // Todo(ed) lifecycle of the video puller and decoder
                     // should be
@@ -302,10 +304,11 @@
                     this_rect.encoding =
                         static_cast<uint8_t>(encoding_type::raw);
                     std::cout << "Encoding is " << this_rect.encoding;
-                    this_rect.data.reserve(this_rect.width * this_rect.height *
-                                           4);
+                    this_rect.data.reserve(
+                        static_cast<std::size_t>(this_rect.width) *
+                        static_cast<std::size_t>(this_rect.height) * 4);
                     std::cout << "Width " << out.width << " Height "
-                               << out.height;
+                              << out.height;
 
                     for (int i = 0; i < out.width * out.height; i++) {
                       auto& pixel = d.OutBuffer[i];
@@ -349,5 +352,5 @@
 
       });
 }
-}
-}
\ No newline at end of file
+}  // namespace kvm
+}  // namespace crow
\ No newline at end of file
diff --git a/include/webassets.hpp b/include/webassets.hpp
new file mode 100644
index 0000000..988af76
--- /dev/null
+++ b/include/webassets.hpp
@@ -0,0 +1,118 @@
+#pragma once
+
+#include <experimental/filesystem>
+#include <fstream>
+#include <string>
+#include <crow/app.h>
+#include <crow/http_request.h>
+#include <crow/http_response.h>
+#include <crow/routing.h>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/container/flat_set.hpp>
+
+namespace crow {
+namespace webassets {
+
+namespace filesystem = std::experimental::filesystem;
+static const char* gzip_string = "gzip";
+static const char* none_string = "none";
+static const char* if_none_match_string = "If-None-Match";
+static const char* content_encoding_string = "Content-Encoding";
+static const char* content_type_string = "Content-Type";
+static const char* etag_string = "ETag";
+
+static boost::container::flat_set<std::string> routes;
+
+template <typename... Middlewares>
+void request_routes(Crow<Middlewares...>& app) {
+  const static boost::container::flat_map<const char*, const char*> content_types{
+      {{".css", "text/css;charset=UTF-8"},
+       {".html", "text/html;charset=UTF-8"},
+       {".js", "text/html;charset=UTF-8"},
+       {".png", "image/png;charset=UTF-8"},
+       {".woff", "application/x-font-woff"},
+       {".woff2", "application/x-font-woff2"},
+       {".ttf", "application/x-font-ttf"},
+       {".svg", "image/svg+xml"},
+       {".eot", "application/vnd.ms-fontobject"},
+       // dev tools don't care about map type, setting to json causes
+       // browser to show as text
+       // https://stackoverflow.com/questions/19911929/what-mime-type-should-i-use-for-javascript-source-map-files
+       {".map", "application/json"}}};
+  auto rootpath = filesystem::path("/usr/share/www/");
+  auto dir_iter = filesystem::recursive_directory_iterator(rootpath);
+  for (auto& dir : dir_iter) {
+    auto absolute_path = dir.path();
+    auto relative_path = filesystem::path(
+        absolute_path.string().substr(rootpath.string().size() - 1));
+    // make sure we don't recurse into certain directories
+    // note: maybe check for is_directory() here as well...
+    if (filesystem::is_directory(dir)) {
+      // don't recurse into hidden directories or symlinks
+      if (boost::starts_with(dir.path().filename().string(), ".") ||
+          filesystem::is_symlink(dir)) {
+        dir_iter.disable_recursion_pending();
+      }
+    } else if (filesystem::is_regular_file(dir)) {
+      auto webpath = relative_path;
+      bool is_gzip = false;
+      if (relative_path.extension() == ".gz") {
+        webpath = webpath.replace_extension("");
+        is_gzip = true;
+      }
+
+      if (webpath.filename() == "index.html") {
+        webpath = webpath.parent_path();
+      }
+
+      routes.insert(webpath.string());
+
+      std::string absolute_path_str = absolute_path.string();
+      const char* content_type = nullptr;
+      auto content_type_it =
+          content_types.find(relative_path.extension().c_str());
+      if (content_type_it != content_types.end()) {
+        content_type = content_type_it->second;
+      }
+      app.route_dynamic(std::string(webpath.string()))(
+          [is_gzip, absolute_path_str, content_type](const crow::request& req,
+                                                     crow::response& res) {
+            if (is_gzip) {
+              res.add_header(content_encoding_string, gzip_string);
+            } else {
+              res.add_header(content_encoding_string, none_string);
+            }
+            // std::string sha1("a576dc96a5c605b28afb032f3103630d61ac1068");
+            // res.add_header(etag_string, sha1);
+
+            // if (req.get_header_value(if_none_match_string) == sha1) {
+            //  res.code = 304;
+            //} else {
+            //  res.code = 200;
+            // TODO, if you have a browser from the dark ages that doesn't
+            // support
+            // gzip, unzip it before sending based on Accept-Encoding header
+            //  res.add_header(content_encoding_string, gzip_string);
+            if (content_type != nullptr) {
+              res.add_header(content_type_string, content_type);
+            }
+            // res.set_header("Cache-Control", "public, max-age=86400");
+            std::ifstream inf(absolute_path_str);
+            if (!inf) {
+              CROW_LOG_DEBUG << "failed to read file";
+              res.code = 400;
+              res.end();
+              return;
+            }
+
+            std::string body{std::istreambuf_iterator<char>(inf),
+                             std::istreambuf_iterator<char>()};
+
+            res.body = body;
+            res.end();
+          });
+    }
+  }
+}
+}  // namespace webassets
+}  // namespace crow
\ No newline at end of file