/*
    GSK - a library to write servers
    Copyright (C) 1999-2000 Dave Benson

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA

    Contact:
        daveb@ffem.org <Dave Benson>
*/


#ifndef __GSK_HTTP_CLIENT_H_
#define __GSK_HTTP_CLIENT_H_

typedef struct _GskHttpClient GskHttpClient;
typedef struct _GskHttpClientClass GskHttpClientClass;


#include "../gskactorbuffered.h"
#include "gskhttpheader.h"

GtkType gsk_http_client_get_type();
#define GSK_TYPE_HTTP_CLIENT			(gsk_http_client_get_type ())
#define GSK_HTTP_CLIENT(obj)              (GTK_CHECK_CAST ((obj), GSK_TYPE_HTTP_CLIENT, GskHttpClient))
#define GSK_HTTP_CLIENT_CLASS(klass)      (GTK_CHECK_CLASS_CAST ((klass), GSK_TYPE_HTTP_CLIENT, GskHttpClientClass))
#define GSK_HTTP_CLIENT_GET_CLASS(obj)    (GSK_HTTP_CLIENT_CLASS(GTK_OBJECT(obj)->klass))
#define GSK_IS_HTTP_CLIENT(obj)           (GTK_CHECK_TYPE ((obj), GSK_TYPE_HTTP_CLIENT))
#define GSK_IS_HTTP_CLIENT_CLASS(klass)   (GTK_CHECK_CLASS_TYPE ((klass), GSK_TYPE_HTTP_CLIENT))


struct _GskHttpClientClass
{
  GskActorBufferedClass	base;
  /*
   * Gets the server's response header.
   */
  gboolean              (*process_response)     (GskHttpClient *client,
                                                 GskHttpHeader *header);

  /*
   * Receive data from server.
   *
   * When length is 0, it means the current wad of content
   * has ended, then the state goes to either KEPTALIVE or ENDED.
   */
  gboolean              (*process_content)      (GskHttpClient *client,
                                                 const char    *content,
                                                 int            length);

  /*
   * Called upon Keepalive.
   */
  void                   (*ready_for_request)   (GskHttpClient *client);
};

/*
 * Client states of an HTTP transaction.
 *
 * GSK_HTTP_CLIENT_CONNECTING       - waiting for connect to finish.
 * GSK_HTTP_CLIENT_CONNECTED        - ready: waiting for request header.
 * GSK_HTTP_CLIENT_POSTING          - writing POST or PUT data to server.
 * GSK_HTTP_CLIENT_READING_RESPONSE - reading header from server.
 * GSK_HTTP_CLIENT_READING_CONTENT  - reading content from server.
 * GSK_HTTP_CLIENT_KEPTALIVE        - done with transaction, waiting...
 * GSK_HTTP_CLIENT_ENDED            - done with transaction.
 *
 *
 *                    CONNECTING
 *                         |
 *                         v
 *                     CONNECTED
 *                         |  by construction, or
 *                         |  via gsk_http_client_do_request()
 *                         v
 *       +---------> WRITING_REQUEST ---------------------------+
 *       |                 |  if POST or PUT,                   |
 *       |                 v                                    |
 *       |              POSTING  (data is supplied              |  GET or HEAD
 *       |                 |   by gsk_http_client_post())       |  requests...
 *       |                 |                                    |
 *       |                 v                                    |
 *       |          READING_RESPONSE <--------------------------+
 *       |                 |
 *       |                 |  once header is complete
 *       |                 v
 *       |          READING_CONTENT
 *       |                 |
 *       |                 |  unless Connection: close or
 *       |                 |  no Connection: line from server.
 *       |                 v
 *  upon +----------- KEPTALIVE
 * gsk_http_client_do_request().
 *   
 */
#define GSK_HTTP_CLIENT_CONNECTING		9300 
#define GSK_HTTP_CLIENT_CONNECTED		9301
#define GSK_HTTP_CLIENT_POSTING			9302
#define GSK_HTTP_CLIENT_READING_RESPONSE	9303
#define GSK_HTTP_CLIENT_READING_CONTENT		9304
#define GSK_HTTP_CLIENT_KEPTALIVE		9305

struct _GskHttpClient 
{
  GskActorBuffered	actor;
  

  /* Private. */
  unsigned		state;
  gboolean		last_was_newline;
  GskBuffer		incoming_buffer;
  GskHttpHeader*        request;
  GskHttpHeader*	response;

  /* used for keep-alive with Content-Length headers. */
  int			content_received;

  /* Using Transfer-Encoding: Chunked. */
  gboolean              chunked;
  /* number of bytes read into the current chunk. */
  int			chunk_current_size;
  /* -1 if we don't currently have a chunk_length. */
  int			chunk_length;
  /* After getting a chunk with length == 0 there is one more
   * terminal newline from the content is over -- this
   * indicates if we are waiting for that newline.
   */
  gboolean              awaiting_newline;

  char		       *chunk_buffer;
  int			chunk_alloced;

  GskBuffer		response_buffer;

  int			amount_to_post;
  int			amount_to_receive;

  GSList               *queued_requests;
};

/*
 * All these functions are to be used by derivers of GskHttpClient only.
 */
void     gsk_http_client_begin_connect (GskHttpClient    *client,
                                        GskSocketAddress *http_host);
/* whether the client can have a request added to it. */
gboolean gsk_http_client_is_ready      (GskHttpClient    *client);
void     gsk_http_client_do_request    (GskHttpClient    *client,
                                        GskHttpHeader    *request);
void     gsk_http_client_post          (GskHttpClient    *client,
                                        const char       *post_data,
				        int               post_length);

#endif
