Ejemplo n.º 1
0
// ----------------------------------------------------------------------------
// the callback used to format data (into text, json, html, etc.) for clients
// ----------------------------------------------------------------------------
// HTTP/1.1 200 OK
// Content-type:text/plain
// Transfer-Encoding: chunked
// 
// 3c;     
// KEY:time VAL:Mon, 12 Mar 2012 13:11:49 GMT|KEY:00 VAL:14.03
// 3c;     
// KEY:time VAL:Mon, 12 Mar 2012 13:11:49 GMT|KEY:00 VAL:40.75
// 3c;     
// KEY:time VAL:Mon, 12 Mar 2012 13:11:49 GMT|KEY:00 VAL:45.02
// 0   
//
static int push_fn(char *argv[], xbuf_t *reply)
{
   reply->len = 0; // empty buffer
   xbuf_xcat(reply, "        \r\n" // room for chunck size: "1a;     "
             "KEY:time VAL:%s|", (char*)get_env(argv, SERVER_DATE));
             
   // fill the 2D HTML <table> cells with our 1D array
   // (we randomly skip some cells to avoid updating all of them)
   {
      int i = 0, r = 0, c = 0;
      while(i < CELLS)
      {
          if(!(sw_rand(&rnd) & 3)) // skip some cells
             xbuf_xcat(reply, "KEY:%c%c VAL:%.02f|",
                              '0' + r, '0' + c, s_data[i++]);

          if(c + 1 < COLS) c++; 
          else
          {
             c = 0;
             if(r + 1 < ROWS) r++; else r = 0;
          }
      }
   }   
   reply->ptr[--reply->len] = 0; // remove the ending '|'
   
   // now we know it, setup the chunk size
   char *p = reply->ptr;
   int i = s_snprintf(p, 8, "%x;", reply->len - (sizeof("        \r\n") - 2));
   p[i] = ' '; // useful for tracing, when we puts(reply->ptr); later
   
   xbuf_ncat(reply, "\r\n", sizeof("\r\n") - 1); // close this chunk
   return 1;   
}
Ejemplo n.º 2
0
int main(int argc, char *argv[])
{
  xbuf_t data;
  xbuf_init(&data);
  xbuf_cat(&data,
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sit amet "
    "quam purus, vitae fermentum turpis. Nam in augue mi. Donec suscipit moles"
    "tie felis, eget rhoncus risus pharetra in. Sed id diam id felis fringilla"
    " adipiscing non vitae odio. Etiam vulputate tristique elit, nec eleifend"
    " mauris scelerisque eu. Vestibulum luctus, enim a luctus posuere, mauris"
    " mauris rutrum mi, eget fermentum massa tortor id enim. Nulla feugiat "
    "porta urna quis laoreet. Morbi metus ante, commodo quis dictum vitae, "
    "rhoncus a libero. Cras viverra feugiat orci id interdum. Duis pulvinar "
    "neque id erat adipiscing facilisis. Maecenas vitae urna risus, euismod"
    " sollicitudin risus."
  );
  
  char gzipped[4096];
  
  u32 len = zlib_cmp(data.ptr, 0, data.len, gzipped, sizeof(gzipped), 1);
  
  printf("Original length: %i\n", data.len);
  printf("Gzipped length:  %i\n\n", len);
  
  xbuf_t *reply = get_reply(argv);
  
  xbuf_xcat(reply,
    "HTTP/1.1 200 OK\r\n"
    "Content-Type: text/plain\r\n"
    "Content-Length: %i\r\n"
    "Content-Encoding: gzip\r\n\r\n",
    len
  );
  
  xbuf_ncat(reply, gzipped, len);
  
  xbuf_free(&data);

  return -1; //custom headers
}
Ejemplo n.º 3
0
Archivo: stream1.c Proyecto: joh-m/gwan
int main(int argc, char *argv[])
{
    // get the server 'reply' buffer where to write our answer to the client
    xbuf_t *reply = get_reply(argv);

    // -------------------------------------------------------------------------
    // step 1: setup a per-request context
    // -------------------------------------------------------------------------
    void **data = (void**)get_env(argv, US_REQUEST_DATA);
    if(!data[0]) // we did not setup our per-request counter yet
    {
        data[0] = (void*)1; // use the persistent pointer as a simple counter

        char head[] = "HTTP/1.1 200 OK\r\n"
                      "Connection: close\r\n"
                      "Content-length: 60\r\n" // 3 * 20 characters
                      "Content-type: text/plain; charset=utf-8\r\n"
                      "\r\n\r\n";
        xbuf_ncat(reply, head, sizeof(head) - 1);
    }

    // -------------------------------------------------------------------------
    // step 2: repeatedly send to client an incremental reply
    // -------------------------------------------------------------------------
    xbuf_xcat(reply, "partial reply %llu<br>\n", data[0]++);

    // -------------------------------------------------------------------------
    // 3: decide when to stop streaming
    // -------------------------------------------------------------------------
    if(data[0] > (void*)NBR_CHUNKS)
    {
        data[0] = 0;
        return RC_NOHEADERS; // RC_NOHEADERS: do not generate HTTP headers
    }

    // RC_NOHEADERS: do not generate HTTP headers
    // RC_STREAMING: call me again after send() is done
    return RC_NOHEADERS + RC_STREAMING;
}
Ejemplo n.º 4
0
// ============================================================================
// main() is receiving the query parameters ("csp?arg1&arg2&arg3...") in argv[]
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
   // create a dynamic buffer and get a pointer on the server response buffer
   xbuf_t *reply = get_reply(argv);

   // -------------------------------------------------------------------------
   // filter input data to avoid all the useless/nasty cases
   // -------------------------------------------------------------------------
   char *Feed  = ""; get_arg("feed=",  &Feed, argc, argv);
   char *Delay = ""; get_arg("delay=", &Delay, argc, argv);

   // -------------------------------------------------------------------------
   // no valid parameters provided, redirect client to "csp_comet.html"
   // -------------------------------------------------------------------------
   if(argc < 1 || !Delay[0] 
   || !Feed[0] || memcmp(Feed, FEED_NAME, sizeof(FEED_NAME)))
   {
      static char redir[] = "HTTP/1.1 302 Found\r\n"
         "Content-type:text/plain\r\n"
         "Location: /csp_comet.html\r\n\r\n"
         "<html><head><title>Redirect</title></head><body>"
         "Click <a href=\"/csp_comet.html\">here</a>.</body></html>";

      xbuf_ncat(reply, redir, sizeof(redir) - 1);
      return 302; // return an HTTP code (302:'Found')
   }
   
   int delay = atoi(Delay);
   if(delay <  1) delay = 1; else
   if(delay > 10) delay = 10; // arbitrary choice for this demo

   // -------------------------------------------------------------------------
   // make this client subscribe to the requested feed
   // -------------------------------------------------------------------------
   // this also creates the feed if it does not exist already
   {
      // internally, make_freq is never > push_freq; here we just define a
      // by-default value aimed at not loading the server pointlessly
      u32 make_freq = 1, push_freq = delay;
      if(!push_list_add(argv, 
                        FEED_NAME, 
                        make_fn, make_freq, // 1 second here (easy to see)
                        push_fn, push_freq, // user-defined
                        free_fn))
         return 503; // 503: Service Unavailable (cannot create feed)
   }
   
   // -------------------------------------------------------------------------
   // send the "Transfer-Encoding: chunked" HTTP header
   // -------------------------------------------------------------------------
   // anatomy of a chunked response:
   // 
   //  HTTP/1.1 200 OK [CRLF]                          <-+
   //  Content-Type: text/html [CRLF]                    | HTTP headers
   //  Transfer-Encoding: chunked [CRLF]               <-+
   //  [CRLF]
   //  1a; optional-stuff-here [CRLF]                  // hexadecimal length
   //  abcdefghijklmnopqrstuvwxyz [CRLF]               // data (ASCII/binary)
   //  10 [CRLF]                                       // hexadecimal length
   //  1234567890abcdef [CRLF]                         // data (ASCII/binary)
   //  0 [CRLF]                                        // 0: end of chunks
   //  optional-footer: some-value [CRLF]              // can be HTTP headers
   //  optional-another-footer: another-value [CRLF]   // can be HTTP headers
   //  [CRLF]
   {
      char head[] = "HTTP/1.1 200 OK\r\n"
                  //"Connection: close\r\n"
                  //"Content-type: application/json; charset=utf-8\r\n"
                    "Content-type: text/plain; charset=utf-8\r\n"
                    "Transfer-Encoding: chunked\r\n\r\n";
      xbuf_ncat(reply, head, sizeof(head) - 1);
   }
   
   // -------------------------------------------------------------------------
   /* send first frame
   // -------------------------------------------------------------------------
   xbuf_t buf;
   xbuf_init(&buf);
   make_fn(argv);   
   push_fn(argv, &buf);
   xbuf_ncat(reply, buf.ptr, buf.len);
   xbuf_free(&buf); */
   
   //puts(reply->ptr);
   return 200; 
}
Ejemplo n.º 5
0
int main(int argc, char *argv[])
{
   // get the server 'reply' buffer where to write our answer to the client
   xbuf_t *reply = get_reply(argv);
   data_t **Data = (data_t **)get_env(argv, US_SERVER_DATA),
         *data;
   data = *Data;

   // -------------------------------------------------------------------------
   // step 1: setup a per-request context
   // -------------------------------------------------------------------------
   gw_req_async_t **R_D = (gw_req_async_t **)get_env(argv, US_REQUEST_DATA)
                  , *r_d;

   if(!*R_D) // we did not setup our per-request structure yet
   {
      // create a per-request memory pool
      if(!gc_init(argv, 4070)) // can now use gc_alloc() for up to 4070 bytes
         return 503; // could not allocate memory!

      *R_D = gc_malloc(argv, sizeof(gw_req_async_t)); // allocate a request context
      if(!*R_D) return 500; // not possible here, but a good habit anyway

      r_d = *R_D;

      r_d->reply = reply;
      r_d->lock.val = 1;

      // ----------------------------------------------------------------------
      // step 2: schedule asynchronous jobs
      // ----------------------------------------------------------------------

      // ev_async_send is not required, only example. 
      ev_async_send(data->loop, &data->async);

      redisAsyncCommand(data->c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
      redisAsyncCommand(data->c, getCallback, r_d, "GET key");
      
      // ----------------------------------------------------------------------
      // tell G-WAN to run the script when reply received from redis
      // ----------------------------------------------------------------------
      // WK_MS:milliseconds, WK_FD:file_descriptor
      wake_up(argv, data->c->c.fd, WK_FD, NULL);
      
      // ----------------------------------------------------------------------
      // send chunked encoding HTTP header and HTTP status code
      // ----------------------------------------------------------------------
      static const char head[] = 
         "HTTP/1.1 200 OK\r\n"
         "Connection: close\r\n" // this will limit the connection rate
         "Content-type: text/html; charset=utf-8\r\n"
         "Transfer-Encoding: chunked\r\n\r\n"
         "5\r\n<pre>\r\n"; // format is: "[length]\r\n[data]\n\n"
      xbuf_ncat(reply, head, sizeof(head) - 1);

      light_lock(&r_d->lock);

      // -------------------------------------------------------------------------
      // return code
      // -------------------------------------------------------------------------
      // RC_NOHEADERS: do not generate HTTP headers
      // RC_STREAMING: call me again after send() is done
      return RC_NOHEADERS + RC_STREAMING; 

   } else {
      r_d = *R_D;

      // We already initialized request wide data pointer
      //  now wait for redis response
      light_lock(&r_d->lock);

      // end chunked encoding; format is: "[length]\r\n[data]\n\n"
      static const char end[] = "6\r\n</pre>\r\n0\r\n\r\n"; 
      xbuf_ncat(reply, end, sizeof(end) - 1);
      
      wake_up(argv, 0, WK_FD, NULL); // 0:disable any further wake-up

      return RC_NOHEADERS; // RC_NOHEADERS: do not generate HTTP headers
   }

   return 200; // never get here
}
Ejemplo n.º 6
0
// ----------------------------------------------------------------------------
// imported functions:
//   get_reply(): get a pointer on the 'reply' dynamic buffer from the server
//       getus(): get current time in microseconds (1 millisecond = 1,000 us)
//     get_env(): get connection's 'environment' variables from the server
//    xbuf_cat(): like strcat(), but it works in the specified dynamic buffer 
//   gif_build(): build an in-memory GIF image from a bitmap and palette
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
   // -------------------------------------------------------------------------
   // get a pointer on the server reply
   // -------------------------------------------------------------------------
   xbuf_t *reply = get_reply(argv);

   // -------------------------------------------------------------------------
   // allocate memory for a raw bitmap
   // -------------------------------------------------------------------------
   int   w = 800, h = 800, nbcolors = 256, wXh = w * h;
   u8 *bmp = (u8*)malloc(wXh);
   if(!bmp) return 503; // service unavailable

   // -------------------------------------------------------------------------
   // render the Mandelbrot set in our bitmap
   // -------------------------------------------------------------------------
   fractals(bmp, w, h, nbcolors);

   // -------------------------------------------------------------------------
   // display the palette (useful when playing with 'tabcol[]' values)
   // -------------------------------------------------------------------------
   {  
      #define ROUND(a) ((a) > 0 ? (int)((a)+0.5) : -(int)(0.5-(a)))
      u8 *p = bmp;
      int i = h, wd20 = w / 20;
      float color = 0, col = (float)nbcolors / (float)h;
      while(i--)
      {
         color += col;
         memset(p, ROUND(color) & 255, wd20);
         p += w;
      }   
   }

   // -------------------------------------------------------------------------
   // build a smooth multi-gradient color palette from the fixed values below
   // -------------------------------------------------------------------------
   static 
   rgb_t tabcol[]={ {  0,   0, 128}, // Med. Blue
                    {  0, 100, 200}, // Light Blue
                    {100, 160, 160}, // Cyan
                    {  0, 220, 100}, // Green
                    {255, 255,   0}, // Yellow
                    {255, 128,   0}, // Orange
                    {128,   0,   0}, // Med. Red
                    { 64,   0,   0}, // Dark Red
                    {128,   0,   0}, // Med. Red
                    {255, 128,   0}, // Orange
                    {255, 255,   0}, // Yellow
                    {  0, 220, 100}, // Green 
                    {100, 160, 160}, // Cyan
                    {  0, 100, 200}, // Light Blue
                    {  0,   0, 128}, // Med. Blue

                    { 64,   0,   0}, // Dark Red
                    {128,   0,   0}, // Med. Red
                    {255, 128,   0}, // Orange
                    {255, 255,   0}, // Yellow
                    {  0, 220, 100}, // Green 
                    {100, 160, 160}, // Cyan
                    {  0, 100, 200}, // Light Blue
                    {  0,   0, 128}, // Med.  Blue
                    {  0,   0,  64}, // Dark  Blue
                  }, *tab = tabcol;
                  
   // -------------------------------------------------------------------------
   // just for fun, select different colors for each call
   // -------------------------------------------------------------------------
   static u32 call = 0;
   u32 ncols = sizeof(tabcol) / sizeof(rgb_t);
   switch(call)
   {
      case 0: tab =  tabcol;     ncols = 10; break; // blue
      case 1: tab = &tabcol[ 4]; ncols = 10; break; // yellow
      case 2: tab = &tabcol[ 7]; ncols =  7; break; // dark red
      case 3: tab = &tabcol[ 1]; ncols = 16; break; // rainbow -
   }
   call = (call + 1) & 3;
   // generate the palette with our defined gradient steps
   u8 pal[768]; dr_gradient(pal, nbcolors, tab, ncols);
   // nice palete but we want a black body to delimit the mandelbrot set
   memset(pal + ((nbcolors - (nbcolors / 16)) * 3), 0, (nbcolors / 16) * 3);

   // -------------------------------------------------------------------------
   // create custom HTTP response headers to send a GIF file
   // -------------------------------------------------------------------------
   // (G-WAN automatically generates headers if none are provided but it can't
   //  guess all MIME types so this automatic feature is for 'text/html' only
   //  ...unless you explicitly specify the reply buffer MIME type)

#ifdef BUILD_CUSTOM_HEADERS // old way of doing things

   // get the current HTTP date (like "Wed, 02 Jun 2010 06:49:37 GMT")
   u8 *date = (u8*)get_env(argv, SERVER_DATE);

   xbuf_xcat(reply,
             "HTTP/1.1 200 OK\r\n"
             "Date: %s\r\n"
             "Last-Modified: %s\r\n"
             "Content-type: image/gif\r\n"
             "Content-Length:       \r\n" // make room for the for GIF length
             "Connection: close\r\n\r\n",
             date, date);

   // -------------------------------------------------------------------------
   // make sure that we have enough space in the 'reply' buffer
   // (we are going to fill it directly from gif_build(), not via xbuf_xxx)
   // -------------------------------------------------------------------------
   // (if we have not enough memory, we will get a 'graceful' crash)
   if(reply->allocated < (wXh / 10)) // very gross approximation
   {
      if(!xbuf_growto(reply, wXh / 10)) // resize reply
      {
         xbuf_init(reply);
         xbuf_ncat(reply, " ", 1);
         reply->len = 0; // discart pointless data, keep allocated memory
         return 503; // error: we could not allocate enough memory
      }
   }

   // -------------------------------------------------------------------------
   // save the place where to patch the void 'Content-Length' HTTP Header
   // -------------------------------------------------------------------------
   char *p = reply->ptr + reply->len
         - (sizeof("\r\nConnection: close\r\n\r\n") - 1);

   // -------------------------------------------------------------------------
   // append a GIF image (-1:no transparency, 0: no comment) to 'reply'
   // -------------------------------------------------------------------------
   int len = gif_build((u8*)(reply->ptr + reply->len), bmp, w, h, 
                       pal, nbcolors, 
                       -1, 0);
   if(len < 0) len = 0; // (len == -1) if gif_build() failed
   reply->len += len;  // add the GIF size to the 'reply' buffer length
   free(bmp);
   
   // -------------------------------------------------------------------------
   // store the GIF size in the empty space of the 'Content-Length' header
   // -------------------------------------------------------------------------
   u32toa(p, len);
   
#else // #ifdef BUILD_CUSTOM_HEADERS // works with any supported MIME type
   
   // -------------------------------------------------------------------------
   // specify a MIME type so we don't have to build custom HTTP headers
   // -------------------------------------------------------------------------
   char *mime = (char*)get_env(argv, REPLY_MIME_TYPE);
   // note that we setup the FILE EXTENTION, not the MIME type:
   mime[0] = '.'; mime[1] = 'g'; mime[2] = 'i'; mime[3] = 'f'; mime[4] = 0; 

   // -------------------------------------------------------------------------
   // make sure that we have enough space in the 'reply' buffer
   // (we are going to fill it directly from gif_build(), not via xbuf_xxx)
   // -------------------------------------------------------------------------
   // (if we have not enough memory, we will get a 'graceful' crash)
   if(reply->allocated < (wXh / 10)) // very gross approximation
   {
      if(!xbuf_growto(reply, wXh / 10)) // resize reply
      {
         xbuf_init(reply);
         xbuf_ncat(reply, " ", 1);
         reply->len = 0; // discart pointless data, keep allocated memory
         return 503; // error: we could not allocate enough memory
      }
   }

   // -------------------------------------------------------------------------
   // append a GIF image (-1:no transparency, 0: no comment) to 'reply'
   // -------------------------------------------------------------------------
   int len = gif_build((u8*)(reply->ptr + reply->len), bmp, w, h, 
                       pal, nbcolors, 
                       -1, 0);
   if(len < 0) len = 0; // (len == -1) if gif_build() failed
   reply->len += len;  // add the GIF size to the 'reply' buffer length
   free(bmp);
   
#endif // #else #ifdef BUILD_CUSTOM_HEADERS

   return 200; // return an HTTP code (200:'OK')
}
Ejemplo n.º 7
0
// ----------------------------------------------------------------------------
// imported functions:
//   get_reply(): get a pointer on the 'reply' dynamic buffer from the server
//     get_env(): get connection's 'environment' variables from the server
//    xbuf_cat(): like strcat(), but it works in the specified dynamic buffer 
//   gif_build(): build an in-memory GIF image from a bitmap and palette
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
   // -------------------------------------------------------------------------
   // build the top of our HTML page
   // -------------------------------------------------------------------------
   static char top[]=
     "<!DOCTYPE HTML>"
     "<html lang=\"en\"><head><title>Captcha</title><meta http-equiv"
     "=\"Content-Type\" content=\"text/html; charset=utf-8\">"
     "<link href=\"/imgs/style.css\" rel=\"stylesheet\" type=\"text/css\">"
     "</head><body style=\"margin:0 16px;\"><br><h2>Captcha for Humans</h2>"
     "<p>Please enter the SUM of all the GREEN FIGURES (not letters) below "
     "(that's twice the same image, just with a different HTML background "
     "- the Data-URI-inlined GIF background is transparent):</p><br>\r\n";

   xbuf_t *reply = get_reply(argv);
   xbuf_ncat(reply, top, sizeof(top) - 1);
   
   // -------------------------------------------------------------------------
   // allocate memory for a raw bitmap
   // -------------------------------------------------------------------------
   const int w = BMP_WIDTH, h = BMP_HEIGHT, wXh = w * h;
   u8 *bmp = (u8*)calloc(CHAR_WIDTH * w, h); 
   if(!bmp) return 503; // service unavailable

   // -------------------------------------------------------------------------
   // render the captcha in our bitmap
   // -------------------------------------------------------------------------
   u32 seed = (u32)getns();
   prnd_t rnd; // pseudo-random generator (period: 1 << 158)
   sw_init(&rnd, seed); // EPOCH time in nano-seconds

   // structure needed by G-WAN's frame buffer routines like dr_text()
   bmp_t img ={ .bmp = bmp, .p = bmp, .bbp = 8, .pen = 1, .bgd = 0, 
                .rect = {0,0, w,h}, .flags = 0, .w = w, .h = h, 
                .x = 0, .y = 0 };
   u32 sum = captcha(&img, &rnd);

   // -------------------------------------------------------------------------
   // build the GIF image, gif_build(0:transparent color index, 0: no comment)
   // -------------------------------------------------------------------------
   u8 pal[] = { 255, 255, 255,  223, 255, 191,  132, 164, 100,  0, 0, 0 };
   const int nbcolors = (sizeof(pal) / sizeof(u8)) / 3; // RGB values

   u8 *gif = (u8*)malloc(CHAR_WIDTH * wXh);
   if(!gif) { free(bmp); return 503; } // service unavailable
   int gln = gif_build(gif, bmp, w, h, pal, nbcolors, 0, 0);

   // -------------------------------------------------------------------------
   // store the base64 encoded GIF in the 'reply' buffer
   // -------------------------------------------------------------------------
   if(gln > 0) // (gln == -1) if gif_build() failed
   {   
      // a real captcha test would only display the first of those two views:
      // (they are shown side-by-side to visualize the background trick)
      xbuf_cat(reply, "<table><tr>\r\n"
                      "<td style=\"background:#dfffbf;\">\r\n");
      u32 img_pos = reply->len;
      xbuf_xcat(reply,
                "<img src=\"data:image/gif;base64,%*B\" alt=\"A tree\" "
                "width=\"%d\" height=\"%d\" /></td>\r\n",
                gln, gif, w + w, h + h); // scale picture
      xbuf_xcat(reply, 
                "<td style=\"background:#84a464;\">%.*s</tr>\r\n</table>\n\r",
                reply->len - img_pos, reply->ptr + img_pos);
   }
   free(gif);
   free(bmp);

   // -------------------------------------------------------------------------
   // close our HTML page
   // -------------------------------------------------------------------------
   xbuf_xcat(reply, 
            "<br>The two sums are: <b>%u</b> and <b>%u</b>... "
            "for the same Captcha image!<br><br>"
            "By just changing the <b>HTML background color</b> (mouse cursor "
            "hovering, previous state or input or shared secret) used for "
            "the transparent GIF Captcha image we can make something simple "
            "for humans become difficult or even completely impossible "
            "for robots.<br><br>"
            "HTML and GIF are served with one single request: the picture"
            " is generated on-the-fly and embedded into the HTML code by "
            " using the base64 encoding (look at the HTML source code)."
            "<br></body></html>", 
            (sum & 0xffff0000) >> 16, 
            sum & 0x0000ffff);

   return 200; // return an HTTP code (200:'OK')
}