libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit fdc3e17e30e655e0a96e261f8c1a6682348db654
parent ac599c6f9c149c26e46cf4dc5a4cd6fd906b1f68
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue,  1 Dec 2015 13:06:38 +0000

killing libmicrospdy

Diffstat:
MChangeLog | 3+++
MMakefile.am | 3---
Mconfigure.ac | 77-----------------------------------------------------------------------------
Dlibmicrospdy.pc.in | 13-------------
Dm4/ax_check_openssl.m4 | 124-------------------------------------------------------------------------------
Msrc/Makefile.am | 16++--------------
Dsrc/datadir/spdy-draft.txt | 2856-------------------------------------------------------------------------------
Msrc/examples/Makefile.am | 40+---------------------------------------
Dsrc/examples/mhd2spdy.c | 322-------------------------------------------------------------------------------
Dsrc/examples/mhd2spdy_http.c | 422-------------------------------------------------------------------------------
Dsrc/examples/mhd2spdy_http.h | 54------------------------------------------------------
Dsrc/examples/mhd2spdy_spdy.c | 1150-------------------------------------------------------------------------------
Dsrc/examples/mhd2spdy_spdy.h | 102-------------------------------------------------------------------------------
Dsrc/examples/mhd2spdy_structures.c | 162-------------------------------------------------------------------------------
Dsrc/examples/mhd2spdy_structures.h | 296-------------------------------------------------------------------------------
Dsrc/examples/spdy_event_loop.c | 487-------------------------------------------------------------------------------
Dsrc/examples/spdy_fileserver.c | 353-------------------------------------------------------------------------------
Dsrc/examples/spdy_response_with_callback.c | 236-------------------------------------------------------------------------------
Msrc/include/Makefile.am | 6+-----
Dsrc/include/microspdy.h | 1380-------------------------------------------------------------------------------
Dsrc/microspdy/Makefile.am | 40----------------------------------------
Dsrc/microspdy/alstructures.c | 41-----------------------------------------
Dsrc/microspdy/alstructures.h | 79-------------------------------------------------------------------------------
Dsrc/microspdy/applicationlayer.c | 748-------------------------------------------------------------------------------
Dsrc/microspdy/applicationlayer.h | 31-------------------------------
Dsrc/microspdy/compression.c | 441-------------------------------------------------------------------------------
Dsrc/microspdy/compression.h | 117-------------------------------------------------------------------------------
Dsrc/microspdy/daemon.c | 544-------------------------------------------------------------------------------
Dsrc/microspdy/daemon.h | 130-------------------------------------------------------------------------------
Dsrc/microspdy/internal.c | 40----------------------------------------
Dsrc/microspdy/internal.h | 199-------------------------------------------------------------------------------
Dsrc/microspdy/io.c | 90-------------------------------------------------------------------------------
Dsrc/microspdy/io.h | 216-------------------------------------------------------------------------------
Dsrc/microspdy/io_openssl.c | 280-------------------------------------------------------------------------------
Dsrc/microspdy/io_openssl.h | 166-------------------------------------------------------------------------------
Dsrc/microspdy/io_raw.c | 194-------------------------------------------------------------------------------
Dsrc/microspdy/io_raw.h | 158-------------------------------------------------------------------------------
Dsrc/microspdy/session.c | 1769-------------------------------------------------------------------------------
Dsrc/microspdy/session.h | 281-------------------------------------------------------------------------------
Dsrc/microspdy/stream.c | 169-------------------------------------------------------------------------------
Dsrc/microspdy/stream.h | 76----------------------------------------------------------------------------
Dsrc/microspdy/structures.c | 638-------------------------------------------------------------------------------
Dsrc/microspdy/structures.h | 1246-------------------------------------------------------------------------------
Dsrc/spdy2http/Makefile.am | 29-----------------------------
Dsrc/spdy2http/proxy.c | 1411-------------------------------------------------------------------------------
Dsrc/testspdy/Makefile.am | 115-------------------------------------------------------------------------------
Dsrc/testspdy/common.c | 59-----------------------------------------------------------
Dsrc/testspdy/common.h | 38--------------------------------------
Dsrc/testspdy/test_daemon_start_stop.c | 49-------------------------------------------------
Dsrc/testspdy/test_daemon_start_stop_many.c | 66------------------------------------------------------------------
Dsrc/testspdy/test_misc.c | 289------------------------------------------------------------------------------
Dsrc/testspdy/test_new_connection.c | 1012-------------------------------------------------------------------------------
Dsrc/testspdy/test_notls.c | 973-------------------------------------------------------------------------------
Dsrc/testspdy/test_proxies.c | 255-------------------------------------------------------------------------------
Dsrc/testspdy/test_request_response.c | 1029-------------------------------------------------------------------------------
Dsrc/testspdy/test_request_response_with_callback.c | 320-------------------------------------------------------------------------------
Dsrc/testspdy/test_requests_with_assets.c | 302------------------------------------------------------------------------------
Dsrc/testspdy/test_session_timeout.c | 338-------------------------------------------------------------------------------
Dsrc/testspdy/test_struct_namevalue.c | 346-------------------------------------------------------------------------------
59 files changed, 7 insertions(+), 22419 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,6 @@ +Tue Dec 1 14:05:13 CET 2015 + SPDY is dead, killing experimental libmicrospdy. -CG + Tue Dec 1 10:01:12 CET 2015 New logic for controlling socket buffer modes. Eliminated delay before last packet in response and before diff --git a/Makefile.am b/Makefile.am @@ -8,9 +8,6 @@ EXTRA_DIST = acinclude.m4 libmicrohttpd.pc.in libmicrospdy.pc.in \ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libmicrohttpd.pc -if ENABLE_SPDY -pkgconfig_DATA += libmicrospdy.pc -endif if BUILD_DOC SUBDIRS += doc diff --git a/configure.ac b/configure.ac @@ -595,77 +595,6 @@ AC_CHECK_LIB([[magic]], [[magic_open]], AM_CONDITIONAL(HAVE_MAGIC, false)) -# optional: libmicrospdy support. Enabled by default if not on W32 -AC_ARG_ENABLE([spdy], - AS_HELP_STRING([--enable-spdy], - [enable build libmicrospdy (yes, no, auto) [auto]]), - [enable_spdy=${enableval}], - [ AS_IF([[test "x$os_is_windows" = "xyes"]], [enable_spdy=no]) ]) - -if test "$enable_spdy" != "no" -then - AX_CHECK_OPENSSL([ have_openssl=yes ],[ have_openssl=no ]) - if test "x$have_openssl" = "xyes" - then - # check OpenSSL headers - SAVE_CPP_FLAGS="$CPPFLAGS" - CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS" - AC_CHECK_HEADERS([openssl/evp.h openssl/rsa.h openssl/rand.h openssl/err.h openssl/sha.h openssl/pem.h openssl/engine.h], [ have_openssl=yes ],[ have_openssl=no ]) - if test "x$have_openssl" = "xyes" - then - # check OpenSSL libs - SAVE_LIBS="$LIBS" - SAVE_LD_FLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" - LIBS="$OPENSSL_LIBS $LIBS" - AC_CHECK_FUNC([SSL_CTX_set_next_protos_advertised_cb], - [ - AC_CHECK_FUNC([SSL_library_init], [ have_openssl=yes ],[ have_openssl=no ]) - ],[ have_openssl=no ]) - LIBS="$SAVE_LIBS" - LDFLAGS="$SAVE_LD_FLAGS" - fi - CPPFLAGS="$SAVE_CPP_FLAGS" - fi - if test "x$have_openssl" = "xyes" - then - enable_spdy=yes - else - AS_IF([[test "x$enable_spdy" = "xyes" ]], [AC_MSG_ERROR([[libmicrospdy cannot be enabled without OpenSSL.]])]) - have_openssl=no - enable_spdy=no - fi -else - # OpenSSL is used only for libmicrospdy - have_openssl=no -fi -AM_CONDITIONAL([HAVE_OPENSSL], [test "x$have_openssl" = "xyes"]) - -if test "$enable_spdy" = "yes" -then - AC_DEFINE([SPDY_SUPPORT],[1],[include libmicrospdy support]) -else - AC_DEFINE([SPDY_SUPPORT],[0],[disable libmicrospdy support]) -fi -AM_CONDITIONAL(ENABLE_SPDY, [test "x$enable_spdy" != "xno"]) -AC_MSG_CHECKING(whether we have OpenSSL and thus can support libmicrospdy) -AC_MSG_RESULT($enable_spdy) - -# for pkg-config -SPDY_LIBDEPS="$OPENSSL_LIBS" - -SPDY_LIB_LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" -SPDY_LIB_CFLAGS="$CFLAGS" -SPDY_LIB_CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS" -AC_SUBST(SPDY_LIB_LDFLAGS) -AC_SUBST(SPDY_LIB_CFLAGS) -AC_SUBST(SPDY_LIB_CPPFLAGS) -# for pkg-config -AC_SUBST(SPDY_LIBDEPS) - -AC_CHECK_HEADERS([spdylay/spdylay.h], [ have_spdylay="yes" ], [have_spdylay="no"]) -AM_CONDITIONAL(HAVE_SPDYLAY, [test "x$have_spdylay" = "xyes"]) - # large file support (> 4 GB) AC_SYS_LARGEFILE AC_FUNC_FSEEKO @@ -946,7 +875,6 @@ AC_SUBST(LDFLAGS) AC_CONFIG_FILES([ libmicrohttpd.pc -libmicrospdy.pc w32/VS2013/microhttpd_dll_res_vc.rc Makefile contrib/Makefile @@ -958,12 +886,9 @@ src/Makefile src/include/Makefile src/platform/Makefile src/microhttpd/Makefile -src/microspdy/Makefile -src/spdy2http/Makefile src/examples/Makefile src/testcurl/Makefile src/testcurl/https/Makefile -src/testspdy/Makefile src/testzzuf/Makefile]) AC_OUTPUT @@ -989,8 +914,6 @@ AC_MSG_NOTICE([libmicrohttpd ${PACKAGE_VERSION} Configuration Summary: epoll support: ${enable_epoll=no} build docs: ${enable_doc} build examples: ${enable_examples} - libmicrospdy: ${enable_spdy} - spdylay (testing): ${have_spdylay} ]) if test "x$enable_https" = "xyes" diff --git a/libmicrospdy.pc.in b/libmicrospdy.pc.in @@ -1,13 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: libmicrospdy -Description: A library for creating an embedded SPDY server -Version: @VERSION@ -Requires: -Conflicts: -Libs: -L${libdir} -lmicrospdy -Libs.private: @SPDY_LIBDEPS@ -Cflags: -I${includedir} diff --git a/m4/ax_check_openssl.m4 b/m4/ax_check_openssl.m4 @@ -1,124 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_check_openssl.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_CHECK_OPENSSL([action-if-found[, action-if-not-found]]) -# -# DESCRIPTION -# -# Look for OpenSSL in a number of default spots, or in a user-selected -# spot (via --with-openssl). Sets -# -# OPENSSL_INCLUDES to the include directives required -# OPENSSL_LIBS to the -l directives required -# OPENSSL_LDFLAGS to the -L or -R flags required -# -# and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately -# -# This macro sets OPENSSL_INCLUDES such that source files should use the -# openssl/ directory in include directives: -# -# #include <openssl/hmac.h> -# -# LICENSE -# -# Copyright (c) 2009,2010 Zmanda Inc. <http://www.zmanda.com/> -# Copyright (c) 2009,2010 Dustin J. Mitchell <dustin@zmanda.com> -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 8 - -AU_ALIAS([CHECK_SSL], [AX_CHECK_OPENSSL]) -AC_DEFUN([AX_CHECK_OPENSSL], [ - found=false - AC_ARG_WITH([openssl], - [AS_HELP_STRING([--with-openssl=DIR], - [root of the OpenSSL directory])], - [ - case "$withval" in - "" | y | ye | yes | n | no) - AC_MSG_ERROR([Invalid --with-openssl value]) - ;; - *) ssldirs="$withval" - ;; - esac - ], [ - # if pkg-config is installed and openssl has installed a .pc file, - # then use that information and don't search ssldirs - AC_PATH_PROG([PKG_CONFIG], [pkg-config]) - if test x"$PKG_CONFIG" != x""; then - OPENSSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null` - if test $? = 0; then - OPENSSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null` - OPENSSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null` - found=true - fi - fi - - # no such luck; use some default ssldirs - if ! $found; then - ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr" - fi - ] - ) - - - # note that we #include <openssl/foo.h>, so the OpenSSL headers have to be in - # an 'openssl' subdirectory - - if ! $found; then - OPENSSL_INCLUDES= - for ssldir in $ssldirs; do - AC_MSG_CHECKING([for openssl/ssl.h in $ssldir]) - if test -f "$ssldir/include/openssl/ssl.h"; then - OPENSSL_INCLUDES="-I$ssldir/include" - OPENSSL_LDFLAGS="-L$ssldir/lib" - OPENSSL_LIBS="-lssl -lcrypto" - found=true - AC_MSG_RESULT([yes]) - break - else - AC_MSG_RESULT([no]) - fi - done - - # if the file wasn't found, well, go ahead and try the link anyway -- maybe - # it will just work! - fi - - # try the preprocessor and linker with our new flags, - # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS - - AC_MSG_CHECKING([whether compiling and linking against OpenSSL works]) - echo "Trying link with OPENSSL_LDFLAGS=$OPENSSL_LDFLAGS;" \ - "OPENSSL_LIBS=$OPENSSL_LIBS; OPENSSL_INCLUDES=$OPENSSL_INCLUDES" >&AS_MESSAGE_LOG_FD - - save_LIBS="$LIBS" - save_LDFLAGS="$LDFLAGS" - save_CPPFLAGS="$CPPFLAGS" - LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" - LIBS="$OPENSSL_LIBS $LIBS" - CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS" - AC_LINK_IFELSE( - [AC_LANG_PROGRAM([#include <openssl/ssl.h>], [SSL_new(NULL)])], - [ - AC_MSG_RESULT([yes]) - $1 - ], [ - AC_MSG_RESULT([no]) - $2 - ]) - CPPFLAGS="$save_CPPFLAGS" - LDFLAGS="$save_LDFLAGS" - LIBS="$save_LIBS" - - AC_SUBST([OPENSSL_INCLUDES]) - AC_SUBST([OPENSSL_LIBS]) - AC_SUBST([OPENSSL_LDFLAGS]) -]) diff --git a/src/Makefile.am b/src/Makefile.am @@ -7,19 +7,8 @@ zzuftests = testzzuf endif endif endif -if ENABLE_SPDY -if HAVE_OPENSSL -microspdy = microspdy -if HAVE_CURL -microspdy += spdy2http -endif -#if HAVE_SPDYLAY -microspdy += testspdy -#endif -endif -endif -SUBDIRS = include platform microhttpd $(microspdy) $(curltests) $(zzuftests) . +SUBDIRS = include platform microhttpd $(curltests) $(zzuftests) . if BUILD_EXAMPLES SUBDIRS += examples @@ -27,5 +16,4 @@ endif EXTRA_DIST = \ datadir/cert-and-key.pem \ - datadir/cert-and-key-for-wireshark.pem \ - datadir/spdy-draft.txt + datadir/cert-and-key-for-wireshark.pem diff --git a/src/datadir/spdy-draft.txt b/src/datadir/spdy-draft.txt @@ -1,2856 +0,0 @@ - - - -Network Working Group M. Belshe -Internet-Draft Twist -Expires: August 4, 2012 R. Peon - Google, Inc - Feb 2012 - - - SPDY Protocol - draft-mbelshe-httpbis-spdy-00 - -Abstract - - This document describes SPDY, a protocol designed for low-latency - transport of content over the World Wide Web. SPDY introduces two - layers of protocol. The lower layer is a general purpose framing - layer which can be used atop a reliable transport (likely TCP) for - multiplexed, prioritized, and compressed data communication of many - concurrent streams. The upper layer of the protocol provides HTTP- - like RFC2616 [RFC2616] semantics for compatibility with existing HTTP - application servers. - -Status of this Memo - - This Internet-Draft is submitted in full conformance with the - provisions of BCP 78 and BCP 79. - - Internet-Drafts are working documents of the Internet Engineering - Task Force (IETF). Note that other groups may also distribute - working documents as Internet-Drafts. The list of current Internet- - Drafts is at http://datatracker.ietf.org/drafts/current/. - - Internet-Drafts are draft documents valid for a maximum of six months - and may be updated, replaced, or obsoleted by other documents at any - time. It is inappropriate to use Internet-Drafts as reference - material or to cite them other than as "work in progress." - - This Internet-Draft will expire on August 4, 2012. - -Copyright Notice - - Copyright (c) 2012 IETF Trust and the persons identified as the - document authors. All rights reserved. - - This document is subject to BCP 78 and the IETF Trust's Legal - Provisions Relating to IETF Documents - (http://trustee.ietf.org/license-info) in effect on the date of - publication of this document. Please review these documents - carefully, as they describe your rights and restrictions with respect - - - -Belshe & Peon Expires August 4, 2012 [Page 1] - -Internet-Draft SPDY Feb 2012 - - - to this document. Code Components extracted from this document must - include Simplified BSD License text as described in Section 4.e of - the Trust Legal Provisions and are provided without warranty as - described in the Simplified BSD License. - - -Table of Contents - - 1. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 - 1.1. Document Organization . . . . . . . . . . . . . . . . . . 4 - 1.2. Definitions . . . . . . . . . . . . . . . . . . . . . . . 5 - 2. SPDY Framing Layer . . . . . . . . . . . . . . . . . . . . . . 6 - 2.1. Session (Connections) . . . . . . . . . . . . . . . . . . 6 - 2.2. Framing . . . . . . . . . . . . . . . . . . . . . . . . . 6 - 2.2.1. Control frames . . . . . . . . . . . . . . . . . . . . 6 - 2.2.2. Data frames . . . . . . . . . . . . . . . . . . . . . 7 - 2.3. Streams . . . . . . . . . . . . . . . . . . . . . . . . . 8 - 2.3.1. Stream frames . . . . . . . . . . . . . . . . . . . . 9 - 2.3.2. Stream creation . . . . . . . . . . . . . . . . . . . 9 - 2.3.3. Stream priority . . . . . . . . . . . . . . . . . . . 10 - 2.3.4. Stream headers . . . . . . . . . . . . . . . . . . . . 10 - 2.3.5. Stream data exchange . . . . . . . . . . . . . . . . . 10 - 2.3.6. Stream half-close . . . . . . . . . . . . . . . . . . 10 - 2.3.7. Stream close . . . . . . . . . . . . . . . . . . . . . 11 - 2.4. Error Handling . . . . . . . . . . . . . . . . . . . . . . 11 - 2.4.1. Session Error Handling . . . . . . . . . . . . . . . . 11 - 2.4.2. Stream Error Handling . . . . . . . . . . . . . . . . 12 - 2.5. Data flow . . . . . . . . . . . . . . . . . . . . . . . . 12 - 2.6. Control frame types . . . . . . . . . . . . . . . . . . . 12 - 2.6.1. SYN_STREAM . . . . . . . . . . . . . . . . . . . . . . 12 - 2.6.2. SYN_REPLY . . . . . . . . . . . . . . . . . . . . . . 14 - 2.6.3. RST_STREAM . . . . . . . . . . . . . . . . . . . . . . 15 - 2.6.4. SETTINGS . . . . . . . . . . . . . . . . . . . . . . . 16 - 2.6.5. PING . . . . . . . . . . . . . . . . . . . . . . . . . 19 - 2.6.6. GOAWAY . . . . . . . . . . . . . . . . . . . . . . . . 20 - 2.6.7. HEADERS . . . . . . . . . . . . . . . . . . . . . . . 21 - 2.6.8. WINDOW_UPDATE . . . . . . . . . . . . . . . . . . . . 22 - 2.6.9. CREDENTIAL . . . . . . . . . . . . . . . . . . . . . . 24 - 2.6.10. Name/Value Header Block . . . . . . . . . . . . . . . 26 - 3. HTTP Layering over SPDY . . . . . . . . . . . . . . . . . . . 33 - 3.1. Connection Management . . . . . . . . . . . . . . . . . . 33 - 3.1.1. Use of GOAWAY . . . . . . . . . . . . . . . . . . . . 33 - 3.2. HTTP Request/Response . . . . . . . . . . . . . . . . . . 34 - 3.2.1. Request . . . . . . . . . . . . . . . . . . . . . . . 34 - 3.2.2. Response . . . . . . . . . . . . . . . . . . . . . . . 35 - 3.2.3. Authentication . . . . . . . . . . . . . . . . . . . . 36 - 3.3. Server Push Transactions . . . . . . . . . . . . . . . . . 37 - 3.3.1. Server implementation . . . . . . . . . . . . . . . . 38 - - - -Belshe & Peon Expires August 4, 2012 [Page 2] - -Internet-Draft SPDY Feb 2012 - - - 3.3.2. Client implementation . . . . . . . . . . . . . . . . 39 - 4. Design Rationale and Notes . . . . . . . . . . . . . . . . . . 40 - 4.1. Separation of Framing Layer and Application Layer . . . . 40 - 4.2. Error handling - Framing Layer . . . . . . . . . . . . . . 40 - 4.3. One Connection Per Domain . . . . . . . . . . . . . . . . 40 - 4.4. Fixed vs Variable Length Fields . . . . . . . . . . . . . 41 - 4.5. Compression Context(s) . . . . . . . . . . . . . . . . . . 41 - 4.6. Unidirectional streams . . . . . . . . . . . . . . . . . . 42 - 4.7. Data Compression . . . . . . . . . . . . . . . . . . . . . 42 - 4.8. Server Push . . . . . . . . . . . . . . . . . . . . . . . 42 - 5. Security Considerations . . . . . . . . . . . . . . . . . . . 43 - 5.1. Use of Same-origin constraints . . . . . . . . . . . . . . 43 - 5.2. HTTP Headers and SPDY Headers . . . . . . . . . . . . . . 43 - 5.3. Cross-Protocol Attacks . . . . . . . . . . . . . . . . . . 43 - 5.4. Server Push Implicit Headers . . . . . . . . . . . . . . . 43 - 6. Privacy Considerations . . . . . . . . . . . . . . . . . . . . 44 - 6.1. Long Lived Connections . . . . . . . . . . . . . . . . . . 44 - 6.2. SETTINGS frame . . . . . . . . . . . . . . . . . . . . . . 44 - 7. Incompatibilities with SPDY draft #2 . . . . . . . . . . . . . 45 - 8. Requirements Notation . . . . . . . . . . . . . . . . . . . . 46 - 9. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 47 - 10. Normative References . . . . . . . . . . . . . . . . . . . . . 48 - Appendix A. Changes . . . . . . . . . . . . . . . . . . . . . . . 50 - Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 51 - - - - - - - - - - - - - - - - - - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 3] - -Internet-Draft SPDY Feb 2012 - - -1. Overview - - One of the bottlenecks of HTTP implementations is that HTTP relies on - multiple connections for concurrency. This causes several problems, - including additional round trips for connection setup, slow-start - delays, and connection rationing by the client, where it tries to - avoid opening too many connections to any single server. HTTP - pipelining helps some, but only achieves partial multiplexing. In - addition, pipelining has proven non-deployable in existing browsers - due to intermediary interference. - - SPDY adds a framing layer for multiplexing multiple, concurrent - streams across a single TCP connection (or any reliable transport - stream). The framing layer is optimized for HTTP-like request- - response streams, such that applications which run over HTTP today - can work over SPDY with little or no change on behalf of the web - application writer. - - The SPDY session offers four improvements over HTTP: - - Multiplexed requests: There is no limit to the number of requests - that can be issued concurrently over a single SPDY connection. - - Prioritized requests: Clients can request certain resources to be - delivered first. This avoids the problem of congesting the - network channel with non-critical resources when a high-priority - request is pending. - - Compressed headers: Clients today send a significant amount of - redundant data in the form of HTTP headers. Because a single web - page may require 50 or 100 subrequests, this data is significant. - - Server pushed streams: Server Push enables content to be pushed - from servers to clients without a request. - - SPDY attempts to preserve the existing semantics of HTTP. All - features such as cookies, ETags, Vary headers, Content-Encoding - negotiations, etc work as they do with HTTP; SPDY only replaces the - way the data is written to the network. - -1.1. Document Organization - - The SPDY Specification is split into two parts: a framing layer - (Section 2), which multiplexes a TCP connection into independent, - length-prefixed frames, and an HTTP layer (Section 3), which - specifies the mechanism for overlaying HTTP request/response pairs on - top of the framing layer. While some of the framing layer concepts - are isolated from the HTTP layer, building a generic framing layer - - - -Belshe & Peon Expires August 4, 2012 [Page 4] - -Internet-Draft SPDY Feb 2012 - - - has not been a goal. The framing layer is tailored to the needs of - the HTTP protocol and server push. - -1.2. Definitions - - client: The endpoint initiating the SPDY session. - - connection: A transport-level connection between two endpoints. - - endpoint: Either the client or server of a connection. - - frame: A header-prefixed sequence of bytes sent over a SPDY - session. - - server: The endpoint which did not initiate the SPDY session. - - session: A synonym for a connection. - - session error: An error on the SPDY session. - - stream: A bi-directional flow of bytes across a virtual channel - within a SPDY session. - - stream error: An error on an individual SPDY stream. - - - - - - - - - - - - - - - - - - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 5] - -Internet-Draft SPDY Feb 2012 - - -2. SPDY Framing Layer - -2.1. Session (Connections) - - The SPDY framing layer (or "session") runs atop a reliable transport - layer such as TCP [RFC0793]. The client is the TCP connection - initiator. SPDY connections are persistent connections. - - For best performance, it is expected that clients will not close open - connections until the user navigates away from all web pages - referencing a connection, or until the server closes the connection. - Servers are encouraged to leave connections open for as long as - possible, but can terminate idle connections if necessary. When - either endpoint closes the transport-level connection, it MUST first - send a GOAWAY (Section 2.6.6) frame so that the endpoints can - reliably determine if requests finished before the close. - -2.2. Framing - - Once the connection is established, clients and servers exchange - framed messages. There are two types of frames: control frames - (Section 2.2.1) and data frames (Section 2.2.2). Frames always have - a common header which is 8 bytes in length. - - The first bit is a control bit indicating whether a frame is a - control frame or data frame. Control frames carry a version number, - a frame type, flags, and a length. Data frames contain the stream - ID, flags, and the length for the payload carried after the common - header. The simple header is designed to make reading and writing of - frames easy. - - All integer values, including length, version, and type, are in - network byte order. SPDY does not enforce alignment of types in - dynamically sized frames. - -2.2.1. Control frames - - +----------------------------------+ - |C| Version(15bits) | Type(16bits) | - +----------------------------------+ - | Flags (8) | Length (24 bits) | - +----------------------------------+ - | Data | - +----------------------------------+ - - Control bit: The 'C' bit is a single bit indicating if this is a - control message. For control frames this value is always 1. - - - - -Belshe & Peon Expires August 4, 2012 [Page 6] - -Internet-Draft SPDY Feb 2012 - - - Version: The version number of the SPDY protocol. This document - describes SPDY version 3. - - Type: The type of control frame. See Control Frames for the complete - list of control frames. - - Flags: Flags related to this frame. Flags for control frames and - data frames are different. - - Length: An unsigned 24-bit value representing the number of bytes - after the length field. - - Data: data associated with this control frame. The format and length - of this data is controlled by the control frame type. - - Control frame processing requirements: - - Note that full length control frames (16MB) can be large for - implementations running on resource-limited hardware. In such - cases, implementations MAY limit the maximum length frame - supported. However, all implementations MUST be able to receive - control frames of at least 8192 octets in length. - -2.2.2. Data frames - - +----------------------------------+ - |C| Stream-ID (31bits) | - +----------------------------------+ - | Flags (8) | Length (24 bits) | - +----------------------------------+ - | Data | - +----------------------------------+ - - Control bit: For data frames this value is always 0. - - Stream-ID: A 31-bit value identifying the stream. - - Flags: Flags related to this frame. Valid flags are: - - 0x01 = FLAG_FIN - signifies that this frame represents the last - frame to be transmitted on this stream. See Stream Close - (Section 2.3.7) below. - - 0x02 = FLAG_COMPRESS - indicates that the data in this frame has - been compressed. - - Length: An unsigned 24-bit value representing the number of bytes - after the length field. The total size of a data frame is 8 bytes + - - - -Belshe & Peon Expires August 4, 2012 [Page 7] - -Internet-Draft SPDY Feb 2012 - - - length. It is valid to have a zero-length data frame. - - Data: The variable-length data payload; the length was defined in the - length field. - - Data frame processing requirements: - - If an endpoint receives a data frame for a stream-id which is not - open and the endpoint has not sent a GOAWAY (Section 2.6.6) frame, - it MUST send issue a stream error (Section 2.4.2) with the error - code INVALID_STREAM for the stream-id. - - If the endpoint which created the stream receives a data frame - before receiving a SYN_REPLY on that stream, it is a protocol - error, and the recipient MUST issue a stream error (Section 2.4.2) - with the status code PROTOCOL_ERROR for the stream-id. - - Implementors note: If an endpoint receives multiple data frames - for invalid stream-ids, it MAY close the session. - - All SPDY endpoints MUST accept compressed data frames. - Compression of data frames is always done using zlib compression. - Each stream initializes and uses its own compression context - dedicated to use within that stream. Endpoints are encouraged to - use application level compression rather than SPDY stream level - compression. - - Each SPDY stream sending compressed frames creates its own zlib - context for that stream, and these compression contexts MUST be - distinct from the compression contexts used with SYN_STREAM/ - SYN_REPLY/HEADER compression. (Thus, if both endpoints of a - stream are compressing data on the stream, there will be two zlib - contexts, one for sending and one for receiving). - -2.3. Streams - - Streams are independent sequences of bi-directional data divided into - frames with several properties: - - Streams may be created by either the client or server. - - Streams optionally carry a set of name/value header pairs. - - Streams can concurrently send data interleaved with other streams. - - Streams may be cancelled. - - - - - -Belshe & Peon Expires August 4, 2012 [Page 8] - -Internet-Draft SPDY Feb 2012 - - -2.3.1. Stream frames - - SPDY defines 3 control frames to manage the lifecycle of a stream: - - SYN_STREAM - Open a new stream - - SYN_REPLY - Remote acknowledgement of a new, open stream - - RST_STREAM - Close a stream - -2.3.2. Stream creation - - A stream is created by sending a control frame with the type set to - SYN_STREAM (Section 2.6.1). If the server is initiating the stream, - the Stream-ID must be even. If the client is initiating the stream, - the Stream-ID must be odd. 0 is not a valid Stream-ID. Stream-IDs - from each side of the connection must increase monotonically as new - streams are created. E.g. Stream 2 may be created after stream 3, - but stream 7 must not be created after stream 9. Stream IDs do not - wrap: when a client or server cannot create a new stream id without - exceeding a 31 bit value, it MUST NOT create a new stream. - - The stream-id MUST increase with each new stream. If an endpoint - receives a SYN_STREAM with a stream id which is less than any - previously received SYN_STREAM, it MUST issue a session error - (Section 2.4.1) with the status PROTOCOL_ERROR. - - It is a protocol error to send two SYN_STREAMs with the same - stream-id. If a recipient receives a second SYN_STREAM for the same - stream, it MUST issue a stream error (Section 2.4.2) with the status - code PROTOCOL_ERROR. - - Upon receipt of a SYN_STREAM, the recipient can reject the stream by - sending a stream error (Section 2.4.2) with the error code - REFUSED_STREAM. Note, however, that the creating endpoint may have - already sent additional frames for that stream which cannot be - immediately stopped. - - Once the stream is created, the creator may immediately send HEADERS - or DATA frames for that stream, without needing to wait for the - recipient to acknowledge. - -2.3.2.1. Unidirectional streams - - When an endpoint creates a stream with the FLAG_UNIDIRECTIONAL flag - set, it creates a unidirectional stream which the creating endpoint - can use to send frames, but the receiving endpoint cannot. The - receiving endpoint is implicitly already in the half-closed - - - -Belshe & Peon Expires August 4, 2012 [Page 9] - -Internet-Draft SPDY Feb 2012 - - - (Section 2.3.6) state. - -2.3.2.2. Bidirectional streams - - SYN_STREAM frames which do not use the FLAG_UNIDIRECTIONAL flag are - bidirectional streams. Both endpoints can send data on a bi- - directional stream. - -2.3.3. Stream priority - - The creator of a stream assigns a priority for that stream. Priority - is represented as an integer from 0 to 7. 0 represents the highest - priority and 7 represents the lowest priority. - - The sender and recipient SHOULD use best-effort to process streams in - the order of highest priority to lowest priority. - -2.3.4. Stream headers - - Streams carry optional sets of name/value pair headers which carry - metadata about the stream. After the stream has been created, and as - long as the sender is not closed (Section 2.3.7) or half-closed - (Section 2.3.6), each side may send HEADERS frame(s) containing the - header data. Header data can be sent in multiple HEADERS frames, and - HEADERS frames may be interleaved with data frames. - -2.3.5. Stream data exchange - - Once a stream is created, it can be used to send arbitrary amounts of - data. Generally this means that a series of data frames will be sent - on the stream until a frame containing the FLAG_FIN flag is set. The - FLAG_FIN can be set on a SYN_STREAM (Section 2.6.1), SYN_REPLY - (Section 2.6.2), HEADERS (Section 2.6.7) or a DATA (Section 2.2.2) - frame. Once the FLAG_FIN has been sent, the stream is considered to - be half-closed. - -2.3.6. Stream half-close - - When one side of the stream sends a frame with the FLAG_FIN flag set, - the stream is half-closed from that endpoint. The sender of the - FLAG_FIN MUST NOT send further frames on that stream. When both - sides have half-closed, the stream is closed. - - If an endpoint receives a data frame after the stream is half-closed - from the sender (e.g. the endpoint has already received a prior frame - for the stream with the FIN flag set), it MUST send a RST_STREAM to - the sender with the status STREAM_ALREADY_CLOSED. - - - - -Belshe & Peon Expires August 4, 2012 [Page 10] - -Internet-Draft SPDY Feb 2012 - - -2.3.7. Stream close - - There are 3 ways that streams can be terminated: - - Normal termination: Normal stream termination occurs when both - sender and recipient have half-closed the stream by sending a - FLAG_FIN. - - Abrupt termination: Either the client or server can send a - RST_STREAM control frame at any time. A RST_STREAM contains an - error code to indicate the reason for failure. When a RST_STREAM - is sent from the stream originator, it indicates a failure to - complete the stream and that no further data will be sent on the - stream. When a RST_STREAM is sent from the stream recipient, the - sender, upon receipt, should stop sending any data on the stream. - The stream recipient should be aware that there is a race between - data already in transit from the sender and the time the - RST_STREAM is received. See Stream Error Handling (Section 2.4.2) - - TCP connection teardown: If the TCP connection is torn down while - un-closed streams exist, then the endpoint must assume that the - stream was abnormally interrupted and may be incomplete. - - If an endpoint receives a data frame after the stream is closed, it - must send a RST_STREAM to the sender with the status PROTOCOL_ERROR. - -2.4. Error Handling - - The SPDY framing layer has only two types of errors, and they are - always handled consistently. Any reference in this specification to - "issue a session error" refers to Section 2.4.1. Any reference to - "issue a stream error" refers to Section 2.4.2. - -2.4.1. Session Error Handling - - A session error is any error which prevents further processing of the - framing layer or which corrupts the session compression state. When - a session error occurs, the endpoint encountering the error MUST - first send a GOAWAY (Section 2.6.6) frame with the stream id of most - recently received stream from the remote endpoint, and the error code - for why the session is terminating. After sending the GOAWAY frame, - the endpoint MUST close the TCP connection. - - Note that the session compression state is dependent upon both - endpoints always processing all compressed data. If an endpoint - partially processes a frame containing compressed data without - updating compression state properly, future control frames which use - compression will be always be errored. Implementations SHOULD always - - - -Belshe & Peon Expires August 4, 2012 [Page 11] - -Internet-Draft SPDY Feb 2012 - - - try to process compressed data so that errors which could be handled - as stream errors do not become session errors. - - Note that because this GOAWAY is sent during a session error case, it - is possible that the GOAWAY will not be reliably received by the - receiving endpoint. It is a best-effort attempt to communicate with - the remote about why the session is going down. - -2.4.2. Stream Error Handling - - A stream error is an error related to a specific stream-id which does - not affect processing of other streams at the framing layer. Upon a - stream error, the endpoint MUST send a RST_STREAM (Section 2.6.3) - frame which contains the stream id of the stream where the error - occurred and the error status which caused the error. After sending - the RST_STREAM, the stream is closed to the sending endpoint. After - sending the RST_STREAM, if the sender receives any frames other than - a RST_STREAM for that stream id, it will result in sending additional - RST_STREAM frames. An endpoint MUST NOT send a RST_STREAM in - response to an RST_STREAM, as doing so would lead to RST_STREAM - loops. Sending a RST_STREAM does not cause the SPDY session to be - closed. - - If an endpoint has multiple RST_STREAM frames to send in succession - for the same stream-id and the same error code, it MAY coalesce them - into a single RST_STREAM frame. (This can happen if a stream is - closed, but the remote sends multiple data frames. There is no - reason to send a RST_STREAM for each frame in succession). - -2.5. Data flow - - Because TCP provides a single stream of data on which SPDY - multiplexes multiple logical streams, clients and servers must - intelligently interleave data messages for concurrent sessions. - -2.6. Control frame types - -2.6.1. SYN_STREAM - - The SYN_STREAM control frame allows the sender to asynchronously - create a stream between the endpoints. See Stream Creation - (Section 2.3.2) - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 12] - -Internet-Draft SPDY Feb 2012 - - -+------------------------------------+ -|1| version | 1 | -+------------------------------------+ -| Flags (8) | Length (24 bits) | -+------------------------------------+ -|X| Stream-ID (31bits) | -+------------------------------------+ -|X| Associated-To-Stream-ID (31bits) | -+------------------------------------+ -| Pri|Unused | Slot | | -+-------------------+ | -| Number of Name/Value pairs (int32) | <+ -+------------------------------------+ | -| Length of name (int32) | | This section is the "Name/Value -+------------------------------------+ | Header Block", and is compressed. -| Name (string) | | -+------------------------------------+ | -| Length of value (int32) | | -+------------------------------------+ | -| Value (string) | | -+------------------------------------+ | -| (repeats) | <+ - - Flags: Flags related to this frame. Valid flags are: - - 0x01 = FLAG_FIN - marks this frame as the last frame to be - transmitted on this stream and puts the sender in the half-closed - (Section 2.3.6) state. - - 0x02 = FLAG_UNIDIRECTIONAL - a stream created with this flag puts - the recipient in the half-closed (Section 2.3.6) state. - - Length: The length is the number of bytes which follow the length - field in the frame. For SYN_STREAM frames, this is 10 bytes plus the - length of the compressed Name/Value block. - - Stream-ID: The 31-bit identifier for this stream. This stream-id - will be used in frames which are part of this stream. - - Associated-To-Stream-ID: The 31-bit identifier for a stream which - this stream is associated to. If this stream is independent of all - other streams, it should be 0. - - Priority: A 3-bit priority (Section 2.3.3) field. - - Unused: 5 bits of unused space, reserved for future use. - - Slot: An 8 bit unsigned integer specifying the index in the server's - - - -Belshe & Peon Expires August 4, 2012 [Page 13] - -Internet-Draft SPDY Feb 2012 - - - CREDENTIAL vector of the client certificate to be used for this - request. see CREDENTIAL frame (Section 2.6.9). The value 0 means no - client certificate should be associated with this stream. - - Name/Value Header Block: A set of name/value pairs carried as part of - the SYN_STREAM. see Name/Value Header Block (Section 2.6.10). - - If an endpoint receives a SYN_STREAM which is larger than the - implementation supports, it MAY send a RST_STREAM with error code - FRAME_TOO_LARGE. All implementations MUST support the minimum size - limits defined in the Control Frames section (Section 2.2.1). - -2.6.2. SYN_REPLY - - SYN_REPLY indicates the acceptance of a stream creation by the - recipient of a SYN_STREAM frame. - -+------------------------------------+ -|1| version | 2 | -+------------------------------------+ -| Flags (8) | Length (24 bits) | -+------------------------------------+ -|X| Stream-ID (31bits) | -+------------------------------------+ -| Number of Name/Value pairs (int32) | <+ -+------------------------------------+ | -| Length of name (int32) | | This section is the "Name/Value -+------------------------------------+ | Header Block", and is compressed. -| Name (string) | | -+------------------------------------+ | -| Length of value (int32) | | -+------------------------------------+ | -| Value (string) | | -+------------------------------------+ | -| (repeats) | <+ - - Flags: Flags related to this frame. Valid flags are: - - 0x01 = FLAG_FIN - marks this frame as the last frame to be - transmitted on this stream and puts the sender in the half-closed - (Section 2.3.6) state. - - Length: The length is the number of bytes which follow the length - field in the frame. For SYN_REPLY frames, this is 4 bytes plus the - length of the compressed Name/Value block. - - Stream-ID: The 31-bit identifier for this stream. - - - - -Belshe & Peon Expires August 4, 2012 [Page 14] - -Internet-Draft SPDY Feb 2012 - - - If an endpoint receives multiple SYN_REPLY frames for the same active - stream ID, it MUST issue a stream error (Section 2.4.2) with the - error code STREAM_IN_USE. - - Name/Value Header Block: A set of name/value pairs carried as part of - the SYN_STREAM. see Name/Value Header Block (Section 2.6.10). - - If an endpoint receives a SYN_REPLY which is larger than the - implementation supports, it MAY send a RST_STREAM with error code - FRAME_TOO_LARGE. All implementations MUST support the minimum size - limits defined in the Control Frames section (Section 2.2.1). - -2.6.3. RST_STREAM - - The RST_STREAM frame allows for abnormal termination of a stream. - When sent by the creator of a stream, it indicates the creator wishes - to cancel the stream. When sent by the recipient of a stream, it - indicates an error or that the recipient did not want to accept the - stream, so the stream should be closed. - - +----------------------------------+ - |1| version | 3 | - +----------------------------------+ - | Flags (8) | 8 | - +----------------------------------+ - |X| Stream-ID (31bits) | - +----------------------------------+ - | Status code | - +----------------------------------+ - - Flags: Flags related to this frame. RST_STREAM does not define any - flags. This value must be 0. - - Length: An unsigned 24-bit value representing the number of bytes - after the length field. For RST_STREAM control frames, this value is - always 8. - - Stream-ID: The 31-bit identifier for this stream. - - Status code: (32 bits) An indicator for why the stream is being - terminated.The following status codes are defined: - - 1 - PROTOCOL_ERROR. This is a generic error, and should only be - used if a more specific error is not available. - - 2 - INVALID_STREAM. This is returned when a frame is received for - a stream which is not active. - - - - -Belshe & Peon Expires August 4, 2012 [Page 15] - -Internet-Draft SPDY Feb 2012 - - - 3 - REFUSED_STREAM. Indicates that the stream was refused before - any processing has been done on the stream. - - 4 - UNSUPPORTED_VERSION. Indicates that the recipient of a stream - does not support the SPDY version requested. - - 5 - CANCEL. Used by the creator of a stream to indicate that the - stream is no longer needed. - - 6 - INTERNAL_ERROR. This is a generic error which can be used - when the implementation has internally failed, not due to anything - in the protocol. - - 7 - FLOW_CONTROL_ERROR. The endpoint detected that its peer - violated the flow control protocol. - - 8 - STREAM_IN_USE. The endpoint received a SYN_REPLY for a stream - already open. - - 9 - STREAM_ALREADY_CLOSED. The endpoint received a data or - SYN_REPLY frame for a stream which is half closed. - - 10 - INVALID_CREDENTIALS. The server received a request for a - resource whose origin does not have valid credentials in the - client certificate vector. - - 11 - FRAME_TOO_LARGE. The endpoint received a frame which this - implementation could not support. If FRAME_TOO_LARGE is sent for - a SYN_STREAM, HEADERS, or SYN_REPLY frame without fully processing - the compressed portion of those frames, then the compression state - will be out-of-sync with the other endpoint. In this case, - senders of FRAME_TOO_LARGE MUST close the session. - - Note: 0 is not a valid status code for a RST_STREAM. - - After receiving a RST_STREAM on a stream, the recipient must not send - additional frames for that stream, and the stream moves into the - closed state. - -2.6.4. SETTINGS - - A SETTINGS frame contains a set of id/value pairs for communicating - configuration data about how the two endpoints may communicate. - SETTINGS frames can be sent at any time by either endpoint, are - optionally sent, and are fully asynchronous. When the server is the - sender, the sender can request that configuration data be persisted - by the client across SPDY sessions and returned to the server in - future communications. - - - -Belshe & Peon Expires August 4, 2012 [Page 16] - -Internet-Draft SPDY Feb 2012 - - - Persistence of SETTINGS ID/Value pairs is done on a per origin/IP - pair (the "origin" is the set of scheme, host, and port from the URI. - See [RFC6454]). That is, when a client connects to a server, and the - server persists settings within the client, the client SHOULD return - the persisted settings on future connections to the same origin AND - IP address and TCP port. Clients MUST NOT request servers to use the - persistence features of the SETTINGS frames, and servers MUST ignore - persistence related flags sent by a client. - - +----------------------------------+ - |1| version | 4 | - +----------------------------------+ - | Flags (8) | Length (24 bits) | - +----------------------------------+ - | Number of entries | - +----------------------------------+ - | ID/Value Pairs | - | ... | - - Control bit: The control bit is always 1 for this message. - - Version: The SPDY version number. - - Type: The message type for a SETTINGS message is 4. - - Flags: FLAG_SETTINGS_CLEAR_SETTINGS (0x1): When set, the client - should clear any previously persisted SETTINGS ID/Value pairs. If - this frame contains ID/Value pairs with the - FLAG_SETTINGS_PERSIST_VALUE set, then the client will first clear its - existing, persisted settings, and then persist the values with the - flag set which are contained within this frame. Because persistence - is only implemented on the client, this flag can only be used when - the sender is the server. - - Length: An unsigned 24-bit value representing the number of bytes - after the length field. The total size of a SETTINGS frame is 8 - bytes + length. - - Number of entries: A 32-bit value representing the number of ID/value - pairs in this message. - - ID: A 32-bit ID number, comprised of 8 bits of flags and 24 bits of - unique ID. - - ID.flags: - - FLAG_SETTINGS_PERSIST_VALUE (0x1): When set, the sender of this - SETTINGS frame is requesting that the recipient persist the ID/ - - - -Belshe & Peon Expires August 4, 2012 [Page 17] - -Internet-Draft SPDY Feb 2012 - - - Value and return it in future SETTINGS frames sent from the - sender to this recipient. Because persistence is only - implemented on the client, this flag is only sent by the - server. - - FLAG_SETTINGS_PERSISTED (0x2): When set, the sender is - notifying the recipient that this ID/Value pair was previously - sent to the sender by the recipient with the - FLAG_SETTINGS_PERSIST_VALUE, and the sender is returning it. - Because persistence is only implemented on the client, this - flag is only sent by the client. - - Defined IDs: - - 1 - SETTINGS_UPLOAD_BANDWIDTH allows the sender to send its - expected upload bandwidth on this channel. This number is an - estimate. The value should be the integral number of kilobytes - per second that the sender predicts as an expected maximum - upload channel capacity. - - 2 - SETTINGS_DOWNLOAD_BANDWIDTH allows the sender to send its - expected download bandwidth on this channel. This number is an - estimate. The value should be the integral number of kilobytes - per second that the sender predicts as an expected maximum - download channel capacity. - - 3 - SETTINGS_ROUND_TRIP_TIME allows the sender to send its - expected round-trip-time on this channel. The round trip time - is defined as the minimum amount of time to send a control - frame from this client to the remote and receive a response. - The value is represented in milliseconds. - - 4 - SETTINGS_MAX_CONCURRENT_STREAMS allows the sender to inform - the remote endpoint the maximum number of concurrent streams - which it will allow. By default there is no limit. For - implementors it is recommended that this value be no smaller - than 100. - - 5 - SETTINGS_CURRENT_CWND allows the sender to inform the - remote endpoint of the current TCP CWND value. - - 6 - SETTINGS_DOWNLOAD_RETRANS_RATE allows the sender to inform - the remote endpoint the retransmission rate (bytes - retransmitted / total bytes transmitted). - - 7 - SETTINGS_INITIAL_WINDOW_SIZE allows the sender to inform - the remote endpoint the initial window size (in bytes) for new - streams. - - - -Belshe & Peon Expires August 4, 2012 [Page 18] - -Internet-Draft SPDY Feb 2012 - - - 8 - SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE allows the server - to inform the client if the new size of the client certificate - vector. - - Value: A 32-bit value. - - The message is intentionally extensible for future information which - may improve client-server communications. The sender does not need - to send every type of ID/value. It must only send those for which it - has accurate values to convey. When multiple ID/value pairs are - sent, they should be sent in order of lowest id to highest id. A - single SETTINGS frame MUST not contain multiple values for the same - ID. If the recipient of a SETTINGS frame discovers multiple values - for the same ID, it MUST ignore all values except the first one. - - A server may send multiple SETTINGS frames containing different ID/ - Value pairs. When the same ID/Value is sent twice, the most recent - value overrides any previously sent values. If the server sends IDs - 1, 2, and 3 with the FLAG_SETTINGS_PERSIST_VALUE in a first SETTINGS - frame, and then sends IDs 4 and 5 with the - FLAG_SETTINGS_PERSIST_VALUE, when the client returns the persisted - state on its next SETTINGS frame, it SHOULD send all 5 settings (1, - 2, 3, 4, and 5 in this example) to the server. - -2.6.5. PING - - The PING control frame is a mechanism for measuring a minimal round- - trip time from the sender. It can be sent from the client or the - server. Recipients of a PING frame should send an identical frame to - the sender as soon as possible (if there is other pending data - waiting to be sent, PING should take highest priority). Each ping - sent by a sender should use a unique ID. - - +----------------------------------+ - |1| version | 6 | - +----------------------------------+ - | 0 (flags) | 4 (length) | - +----------------------------------| - | 32-bit ID | - +----------------------------------+ - - Control bit: The control bit is always 1 for this message. - - Version: The SPDY version number. - - Type: The message type for a PING message is 6. - - Length: This frame is always 4 bytes long. - - - -Belshe & Peon Expires August 4, 2012 [Page 19] - -Internet-Draft SPDY Feb 2012 - - - ID: A unique ID for this ping, represented as an unsigned 32 bit - value. When the client initiates a ping, it must use an odd numbered - ID. When the server initiates a ping, it must use an even numbered - ping. Use of odd/even IDs is required in order to avoid accidental - looping on PINGs (where each side initiates an identical PING at the - same time). - - Note: If a sender uses all possible PING ids (e.g. has sent all 2^31 - possible IDs), it can wrap and start re-using IDs. - - If a server receives an even numbered PING which it did not initiate, - it must ignore the PING. If a client receives an odd numbered PING - which it did not initiate, it must ignore the PING. - -2.6.6. GOAWAY - - The GOAWAY control frame is a mechanism to tell the remote side of - the connection to stop creating streams on this session. It can be - sent from the client or the server. Once sent, the sender will not - respond to any new SYN_STREAMs on this session. Recipients of a - GOAWAY frame must not send additional streams on this session, - although a new session can be established for new streams. The - purpose of this message is to allow an endpoint to gracefully stop - accepting new streams (perhaps for a reboot or maintenance), while - still finishing processing of previously established streams. - - There is an inherent race condition between an endpoint sending - SYN_STREAMs and the remote sending a GOAWAY message. To deal with - this case, the GOAWAY contains a last-stream-id indicating the - stream-id of the last stream which was created on the sending - endpoint in this session. If the receiver of the GOAWAY sent new - SYN_STREAMs for sessions after this last-stream-id, they were not - processed by the server and the receiver may treat the stream as - though it had never been created at all (hence the receiver may want - to re-create the stream later on a new session). - - Endpoints should always send a GOAWAY message before closing a - connection so that the remote can know whether a stream has been - partially processed or not. (For example, if an HTTP client sends a - POST at the same time that a server closes a connection, the client - cannot know if the server started to process that POST request if the - server does not send a GOAWAY frame to indicate where it stopped - working). - - After sending a GOAWAY message, the sender must ignore all SYN_STREAM - frames for new streams. - - - - - -Belshe & Peon Expires August 4, 2012 [Page 20] - -Internet-Draft SPDY Feb 2012 - - - +----------------------------------+ - |1| version | 7 | - +----------------------------------+ - | 0 (flags) | 8 (length) | - +----------------------------------| - |X| Last-good-stream-ID (31 bits) | - +----------------------------------+ - | Status code | - +----------------------------------+ - - Control bit: The control bit is always 1 for this message. - - Version: The SPDY version number. - - Type: The message type for a GOAWAY message is 7. - - Length: This frame is always 8 bytes long. - - Last-good-stream-Id: The last stream id which was replied to (with - either a SYN_REPLY or RST_STREAM) by the sender of the GOAWAY - message. If no streams were replied to, this value MUST be 0. - - Status: The reason for closing the session. - - 0 - OK. This is a normal session teardown. - - 1 - PROTOCOL_ERROR. This is a generic error, and should only be - used if a more specific error is not available. - - 11 - INTERNAL_ERROR. This is a generic error which can be used - when the implementation has internally failed, not due to anything - in the protocol. - -2.6.7. HEADERS - - The HEADERS frame augments a stream with additional headers. It may - be optionally sent on an existing stream at any time. Specific - application of the headers in this frame is application-dependent. - The name/value header block within this frame is compressed. - - - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 21] - -Internet-Draft SPDY Feb 2012 - - -+------------------------------------+ -|1| version | 8 | -+------------------------------------+ -| Flags (8) | Length (24 bits) | -+------------------------------------+ -|X| Stream-ID (31bits) | -+------------------------------------+ -| Number of Name/Value pairs (int32) | <+ -+------------------------------------+ | -| Length of name (int32) | | This section is the "Name/Value -+------------------------------------+ | Header Block", and is compressed. -| Name (string) | | -+------------------------------------+ | -| Length of value (int32) | | -+------------------------------------+ | -| Value (string) | | -+------------------------------------+ | -| (repeats) | <+ - - Flags: Flags related to this frame. Valid flags are: - - 0x01 = FLAG_FIN - marks this frame as the last frame to be - transmitted on this stream and puts the sender in the half-closed - (Section 2.3.6) state. - - Length: An unsigned 24 bit value representing the number of bytes - after the length field. The minimum length of the length field is 4 - (when the number of name value pairs is 0). - - Stream-ID: The stream this HEADERS block is associated with. - - Name/Value Header Block: A set of name/value pairs carried as part of - the SYN_STREAM. see Name/Value Header Block (Section 2.6.10). - -2.6.8. WINDOW_UPDATE - - The WINDOW_UPDATE control frame is used to implement per stream flow - control in SPDY. Flow control in SPDY is per hop, that is, only - between the two endpoints of a SPDY connection. If there are one or - more intermediaries between the client and the origin server, flow - control signals are not explicitly forwarded by the intermediaries. - (However, throttling of data transfer by any recipient may have the - effect of indirectly propagating flow control information upstream - back to the original sender.) Flow control only applies to the data - portion of data frames. Recipients must buffer all control frames. - If a recipient fails to buffer an entire control frame, it MUST issue - a stream error (Section 2.4.2) with the status code - FLOW_CONTROL_ERROR for the stream. - - - -Belshe & Peon Expires August 4, 2012 [Page 22] - -Internet-Draft SPDY Feb 2012 - - - Flow control in SPDY is implemented by a data transfer window kept by - the sender of each stream. The data transfer window is a simple - uint32 that indicates how many bytes of data the sender can transmit. - After a stream is created, but before any data frames have been - transmitted, the sender begins with the initial window size. This - window size is a measure of the buffering capability of the - recipient. The sender must not send a data frame with data length - greater than the transfer window size. After sending each data - frame, the sender decrements its transfer window size by the amount - of data transmitted. When the window size becomes less than or equal - to 0, the sender must pause transmitting data frames. At the other - end of the stream, the recipient sends a WINDOW_UPDATE control back - to notify the sender that it has consumed some data and freed up - buffer space to receive more data. - - +----------------------------------+ - |1| version | 9 | - +----------------------------------+ - | 0 (flags) | 8 (length) | - +----------------------------------+ - |X| Stream-ID (31-bits) | - +----------------------------------+ - |X| Delta-Window-Size (31-bits) | - +----------------------------------+ - - Control bit: The control bit is always 1 for this message. - - Version: The SPDY version number. - - Type: The message type for a WINDOW_UPDATE message is 9. - - Length: The length field is always 8 for this frame (there are 8 - bytes after the length field). - - Stream-ID: The stream ID that this WINDOW_UPDATE control frame is - for. - - Delta-Window-Size: The additional number of bytes that the sender can - transmit in addition to existing remaining window size. The legal - range for this field is 1 to 2^31 - 1 (0x7fffffff) bytes. - - The window size as kept by the sender must never exceed 2^31 - (although it can become negative in one special case). If a sender - receives a WINDOW_UPDATE that causes the its window size to exceed - this limit, it must send RST_STREAM with status code - FLOW_CONTROL_ERROR to terminate the stream. - - When a SPDY connection is first established, the default initial - - - -Belshe & Peon Expires August 4, 2012 [Page 23] - -Internet-Draft SPDY Feb 2012 - - - window size for all streams is 64KB. An endpoint can use the - SETTINGS control frame to adjust the initial window size for the - connection. That is, its peer can start out using the 64KB default - initial window size when sending data frames before receiving the - SETTINGS. Because SETTINGS is asynchronous, there may be a race - condition if the recipient wants to decrease the initial window size, - but its peer immediately sends 64KB on the creation of a new - connection, before waiting for the SETTINGS to arrive. This is one - case where the window size kept by the sender will become negative. - Once the sender detects this condition, it must stop sending data - frames and wait for the recipient to catch up. The recipient has two - choices: - - immediately send RST_STREAM with FLOW_CONTROL_ERROR status code. - - allow the head of line blocking (as there is only one stream for - the session and the amount of data in flight is bounded by the - default initial window size), and send WINDOW_UPDATE as it - consumes data. - - In the case of option 2, both sides must compute the window size - based on the initial window size in the SETTINGS. For example, if - the recipient sets the initial window size to be 16KB, and the sender - sends 64KB immediately on connection establishment, the sender will - discover its window size is -48KB on receipt of the SETTINGS. As the - recipient consumes the first 16KB, it must send a WINDOW_UPDATE of - 16KB back to the sender. This interaction continues until the - sender's window size becomes positive again, and it can resume - transmitting data frames. - - After the recipient reads in a data frame with FLAG_FIN that marks - the end of the data stream, it should not send WINDOW_UPDATE frames - as it consumes the last data frame. A sender should ignore all the - WINDOW_UPDATE frames associated with the stream after it send the - last frame for the stream. - - The data frames from the sender and the WINDOW_UPDATE frames from the - recipient are completely asynchronous with respect to each other. - This property allows a recipient to aggressively update the window - size kept by the sender to prevent the stream from stalling. - -2.6.9. CREDENTIAL - - The CREDENTIAL control frame is used by the client to send additional - client certificates to the server. A SPDY client may decide to send - requests for resources from different origins on the same SPDY - session if it decides that that server handles both origins. For - example if the IP address associated with both hostnames matches and - - - -Belshe & Peon Expires August 4, 2012 [Page 24] - -Internet-Draft SPDY Feb 2012 - - - the SSL server certificate presented in the initial handshake is - valid for both hostnames. However, because the SSL connection can - contain at most one client certificate, the client needs a mechanism - to send additional client certificates to the server. - - The server is required to maintain a vector of client certificates - associated with a SPDY session. When the client needs to send a - client certificate to the server, it will send a CREDENTIAL frame - that specifies the index of the slot in which to store the - certificate as well as proof that the client posesses the - corresponding private key. The initial size of this vector must be - 8. If the client provides a client certificate during the first TLS - handshake, the contents of this certificate must be copied into the - first slot (index 1) in the CREDENTIAL vector, though it may be - overwritten by subsequent CREDENTIAL frames. The server must - exclusively use the CREDNETIAL vector when evaluating the client - certificates associated with an origin. The server may change the - size of this vector by sending a SETTINGS frame with the setting - SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE value specified. In the - event that the new size is smaller than the current size, truncation - occurs preserving lower-index slots as possible. - - TLS renegotiation with client authentication is incompatible with - SPDY given the multiplexed nature of SPDY. Specifically, imagine - that the client has 2 requests outstanding to the server for two - different pages (in different tabs). When the renegotiation + client - certificate request comes in, the browser is unable to determine - which resource triggered the client certificate request, in order to - prompt the user accordingly. - - +----------------------------------+ - |1|000000000000001|0000000000001011| - +----------------------------------+ - | flags (8) | Length (24 bits) | - +----------------------------------+ - | Slot (16 bits) | | - +-----------------+ | - | Proof Length (32 bits) | - +----------------------------------+ - | Proof | - +----------------------------------+ <+ - | Certificate Length (32 bits) | | - +----------------------------------+ | Repeated until end of frame - | Certificate | | - +----------------------------------+ <+ - - Slot: The index in the server's client certificate vector where this - certificate should be stored. If there is already a certificate - - - -Belshe & Peon Expires August 4, 2012 [Page 25] - -Internet-Draft SPDY Feb 2012 - - - stored at this index, it will be overwritten. The index is one - based, not zero based; zero is an invalid slot index. - - Proof: Cryptographic proof that the client has possession of the - private key associated with the certificate. The format is a TLS - digitally-signed element - (http://tools.ietf.org/html/rfc5246#section-4.7). The signature - algorithm must be the same as that used in the CertificateVerify - message. However, since the MD5+SHA1 signature type used in TLS 1.0 - connections can not be correctly encoded in a digitally-signed - element, SHA1 must be used when MD5+SHA1 was used in the SSL - connection. The signature is calculated over a 32 byte TLS extractor - value (http://tools.ietf.org/html/rfc5705) with a label of "EXPORTER - SPDY certificate proof" using the empty string as context. ForRSA - certificates the signature would be a PKCS#1 v1.5 signature. For - ECDSA, it would be an ECDSA-Sig-Value - (http://tools.ietf.org/html/rfc5480#appendix-A). For a 1024-bit RSA - key, the CREDENTIAL message would be ~500 bytes. - - Certificate: The certificate chain, starting with the leaf - certificate. Each certificate must be encoded as a 32 bit length, - followed by a DER encoded certificate. The certificate must be of - the same type (RSA, ECDSA, etc) as the client certificate associated - with the SSL connection. - - If the server receives a request for a resource with unacceptable - credential (either missing or invalid), it must reply with a - RST_STREAM frame with the status code INVALID_CREDENTIALS. Upon - receipt of a RST_STREAM frame with INVALID_CREDENTIALS, the client - should initiate a new stream directly to the requested origin and - resend the request. Note, SPDY does not allow the server to request - different client authentication for different resources in the same - origin. - - If the server receives an invalid CREDENTIAL frame, it MUST respond - with a GOAWAY frame and shutdown the session. - -2.6.10. Name/Value Header Block - - The Name/Value Header Block is found in the SYN_STREAM, SYN_REPLY and - HEADERS control frames, and shares a common format: - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 26] - -Internet-Draft SPDY Feb 2012 - - - +------------------------------------+ - | Number of Name/Value pairs (int32) | - +------------------------------------+ - | Length of name (int32) | - +------------------------------------+ - | Name (string) | - +------------------------------------+ - | Length of value (int32) | - +------------------------------------+ - | Value (string) | - +------------------------------------+ - | (repeats) | - - Number of Name/Value pairs: The number of repeating name/value pairs - following this field. - - List of Name/Value pairs: - - Length of Name: a 32-bit value containing the number of octets in - the name field. Note that in practice, this length must not - exceed 2^24, as that is the maximum size of a SPDY frame. - - Name: 0 or more octets, 8-bit sequences of data, excluding 0. - - Length of Value: a 32-bit value containing the number of octets in - the value field. Note that in practice, this length must not - exceed 2^24, as that is the maximum size of a SPDY frame. - - Value: 0 or more octets, 8-bit sequences of data, excluding 0. - - Each header name must have at least one value. Header names are - encoded using the US-ASCII character set [ASCII] and must be all - lower case. The length of each name must be greater than zero. A - recipient of a zero-length name MUST issue a stream error - (Section 2.4.2) with the status code PROTOCOL_ERROR for the - stream-id. - - Duplicate header names are not allowed. To send two identically - named headers, send a header with two values, where the values are - separated by a single NUL (0) byte. A header value can either be - empty (e.g. the length is zero) or it can contain multiple, NUL- - separated values, each with length greater than zero. The value - never starts nor ends with a NUL character. Recipients of illegal - value fields MUST issue a stream error (Section 2.4.2) with the - status code PROTOCOL_ERROR for the stream-id. - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 27] - -Internet-Draft SPDY Feb 2012 - - -2.6.10.1. Compression - - The Name/Value Header Block is a section of the SYN_STREAM, - SYN_REPLY, and HEADERS frames used to carry header meta-data. This - block is always compressed using zlib compression. Within this - specification, any reference to 'zlib' is referring to the ZLIB - Compressed Data Format Specification Version 3.3 as part of RFC1950. - [RFC1950] - - For each HEADERS compression instance, the initial state is - initialized using the following dictionary [UDELCOMPRESSION]: - - const unsigned char SPDY_dictionary_txt[] = { - 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, \\ - - - - o p t i - 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, \\ o n s - - - - h - 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, \\ e a d - - - - p - 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, \\ o s t - - - - p - 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, \\ u t - - - - d e - 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, \\ l e t e - - - - - 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, \\ t r a c e - - - - 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, \\ - a c c e p t - - 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, \\ - - - a c c e p - 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, \\ t - c h a r s e - 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, \\ t - - - - a c c - 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, \\ e p t - e n c o - 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, \\ d i n g - - - - - 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, \\ a c c e p t - l - 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, \\ a n g u a g e - - 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, \\ - - - a c c e p - 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, \\ t - r a n g e s - 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, \\ - - - - a g e - - 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, \\ - - - a l l o w - 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, \\ - - - - a u t h - 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, \\ o r i z a t i o - 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, \\ n - - - - c a c - 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, \\ h e - c o n t r - 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, \\ o l - - - - c o - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, \\ n n e c t i o n - 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, \\ - - - - c o n t - 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, \\ e n t - b a s e - 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, \\ - - - - c o n t - 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, \\ e n t - e n c o - 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, \\ d i n g - - - - - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, \\ c o n t e n t - - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, \\ l a n g u a g e - 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, \\ - - - - c o n t - 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, \\ e n t - l e n g - 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, \\ t h - - - - c o - - - -Belshe & Peon Expires August 4, 2012 [Page 28] - -Internet-Draft SPDY Feb 2012 - - - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, \\ n t e n t - l o - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, \\ c a t i o n - - - 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, \\ - - c o n t e n - 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, \\ t - m d 5 - - - - 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, \\ - c o n t e n t - 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, \\ - r a n g e - - - 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, \\ - - c o n t e n - 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, \\ t - t y p e - - - 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, \\ - - d a t e - - - 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, \\ - - e t a g - - - 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, \\ - - e x p e c t - 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, \\ - - - - e x p i - 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, \\ r e s - - - - f - 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, \\ r o m - - - - h - 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, \\ o s t - - - - i - 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, \\ f - m a t c h - - 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, \\ - - - i f - m o - 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, \\ d i f i e d - s - 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, \\ i n c e - - - - - 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, \\ i f - n o n e - - 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, \\ m a t c h - - - - 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, \\ - i f - r a n g - 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, \\ e - - - - i f - - 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, \\ u n m o d i f i - 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, \\ e d - s i n c e - 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, \\ - - - - l a s t - 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, \\ - m o d i f i e - 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, \\ d - - - - l o c - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, \\ a t i o n - - - - 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, \\ - m a x - f o r - 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, \\ w a r d s - - - - 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, \\ - p r a g m a - - 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, \\ - - - p r o x y - 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, \\ - a u t h e n t - 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, \\ i c a t e - - - - 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, \\ - p r o x y - a - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, \\ u t h o r i z a - 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, \\ t i o n - - - - - 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, \\ r a n g e - - - - 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, \\ - r e f e r e r - 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, \\ - - - - r e t r - 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, \\ y - a f t e r - - 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, \\ - - - s e r v e - 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, \\ r - - - - t e - - 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, \\ - - - t r a i l - 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, \\ e r - - - - t r - 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, \\ a n s f e r - e - 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, \\ n c o d i n g - - - - -Belshe & Peon Expires August 4, 2012 [Page 29] - -Internet-Draft SPDY Feb 2012 - - - 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, \\ - - - u p g r a - 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, \\ d e - - - - u s - 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, \\ e r - a g e n t - 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, \\ - - - - v a r y - 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, \\ - - - - v i a - - 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, \\ - - - w a r n i - 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, \\ n g - - - - w w - 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, \\ w - a u t h e n - 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, \\ t i c a t e - - - 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, \\ - - m e t h o d - 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, \\ - - - - g e t - - 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, \\ - - - s t a t u - 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, \\ s - - - - 2 0 0 - 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, \\ - O K - - - - v - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, \\ e r s i o n - - - 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, \\ - - H T T P - 1 - 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, \\ - 1 - - - - u r - 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, \\ l - - - - p u b - 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, \\ l i c - - - - s - 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, \\ e t - c o o k i - 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, \\ e - - - - k e e - 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, \\ p - a l i v e - - 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, \\ - - - o r i g i - 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, \\ n 1 0 0 1 0 1 2 - 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, \\ 0 1 2 0 2 2 0 5 - 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, \\ 2 0 6 3 0 0 3 0 - 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, \\ 2 3 0 3 3 0 4 3 - 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, \\ 0 5 3 0 6 3 0 7 - 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, \\ 4 0 2 4 0 5 4 0 - 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, \\ 6 4 0 7 4 0 8 4 - 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, \\ 0 9 4 1 0 4 1 1 - 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, \\ 4 1 2 4 1 3 4 1 - 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, \\ 4 4 1 5 4 1 6 4 - 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, \\ 1 7 5 0 2 5 0 4 - 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, \\ 5 0 5 2 0 3 - N - 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, \\ o n - A u t h o - 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, \\ r i t a t i v e - 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, \\ - I n f o r m a - 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, \\ t i o n 2 0 4 - - 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, \\ N o - C o n t e - 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, \\ n t 3 0 1 - M o - 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, \\ v e d - P e r m - 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, \\ a n e n t l y 4 - 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, \\ 0 0 - B a d - R - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, \\ e q u e s t 4 0 - 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, \\ 1 - U n a u t h - 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, \\ o r i z e d 4 0 - 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, \\ 3 - F o r b i d - - - -Belshe & Peon Expires August 4, 2012 [Page 30] - -Internet-Draft SPDY Feb 2012 - - - 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, \\ d e n 4 0 4 - N - 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, \\ o t - F o u n d - 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, \\ 5 0 0 - I n t e - 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, \\ r n a l - S e r - 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, \\ v e r - E r r o - 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, \\ r 5 0 1 - N o t - 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, \\ - I m p l e m e - 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, \\ n t e d 5 0 3 - - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, \\ S e r v i c e - - 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, \\ U n a v a i l a - 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, \\ b l e J a n - F - 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, \\ e b - M a r - A - 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, \\ p r - M a y - J - 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, \\ u n - J u l - A - 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, \\ u g - S e p t - - 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, \\ O c t - N o v - - 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, \\ D e c - 0 0 - 0 - 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, \\ 0 - 0 0 - M o n - 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, \\ - - T u e - - W - 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, \\ e d - - T h u - - 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, \\ - F r i - - S a - 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, \\ t - - S u n - - - 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, \\ G M T c h u n k - 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, \\ e d - t e x t - - 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, \\ h t m l - i m a - 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, \\ g e - p n g - i - 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, \\ m a g e - j p g - 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, \\ - i m a g e - g - 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, \\ i f - a p p l i - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, \\ c a t i o n - x - 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, \\ m l - a p p l i - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, \\ c a t i o n - x - 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, \\ h t m l - x m l - 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, \\ - t e x t - p l - 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, \\ a i n - t e x t - 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, \\ - j a v a s c r - 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, \\ i p t - p u b l - 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, \\ i c p r i v a t - 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, \\ e m a x - a g e - 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, \\ - g z i p - d e - 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, \\ f l a t e - s d - 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, \\ c h c h a r s e - 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, \\ t - u t f - 8 c - 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, \\ h a r s e t - i - 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, \\ s o - 8 8 5 9 - - 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, \\ 1 - u t f - - - - 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e \\ - e n q - 0 - - }; - - - -Belshe & Peon Expires August 4, 2012 [Page 31] - -Internet-Draft SPDY Feb 2012 - - - The entire contents of the name/value header block is compressed - using zlib. There is a single zlib stream for all name value pairs - in one direction on a connection. SPDY uses a SYNC_FLUSH between - each compressed frame. - - Implementation notes: the compression engine can be tuned to favor - speed or size. Optimizing for size increases memory use and CPU - consumption. Because header blocks are generally small, implementors - may want to reduce the window-size of the compression engine from the - default 15bits (a 32KB window) to more like 11bits (a 2KB window). - The exact setting is chosen by the compressor, the decompressor will - work with any setting. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 32] - -Internet-Draft SPDY Feb 2012 - - -3. HTTP Layering over SPDY - - SPDY is intended to be as compatible as possible with current web- - based applications. This means that, from the perspective of the - server business logic or application API, the features of HTTP are - unchanged. To achieve this, all of the application request and - response header semantics are preserved, although the syntax of - conveying those semantics has changed. Thus, the rules from the - HTTP/1.1 specification in RFC2616 [RFC2616] apply with the changes in - the sections below. - -3.1. Connection Management - - Clients SHOULD NOT open more than one SPDY session to a given origin - [RFC6454] concurrently. - - Note that it is possible for one SPDY session to be finishing (e.g. a - GOAWAY message has been sent, but not all streams have finished), - while another SPDY session is starting. - -3.1.1. Use of GOAWAY - - SPDY provides a GOAWAY message which can be used when closing a - connection from either the client or server. Without a server GOAWAY - message, HTTP has a race condition where the client sends a request - (a new SYN_STREAM) just as the server is closing the connection, and - the client cannot know if the server received the stream or not. By - using the last-stream-id in the GOAWAY, servers can indicate to the - client if a request was processed or not. - - Note that some servers will choose to send the GOAWAY and immediately - terminate the connection without waiting for active streams to - finish. The client will be able to determine this because SPDY - streams are determinstically closed. This abrupt termination will - force the client to heuristically decide whether to retry the pending - requests. Clients always need to be capable of dealing with this - case because they must deal with accidental connection termination - cases, which are the same as the server never having sent a GOAWAY. - - More sophisticated servers will use GOAWAY to implement a graceful - teardown. They will send the GOAWAY and provide some time for the - active streams to finish before terminating the connection. - - If a SPDY client closes the connection, it should also send a GOAWAY - message. This allows the server to know if any server-push streams - were received by the client. - - If the endpoint closing the connection has not received any - - - -Belshe & Peon Expires August 4, 2012 [Page 33] - -Internet-Draft SPDY Feb 2012 - - - SYN_STREAMs from the remote, the GOAWAY will contain a last-stream-id - of 0. - -3.2. HTTP Request/Response - -3.2.1. Request - - The client initiates a request by sending a SYN_STREAM frame. For - requests which do not contain a body, the SYN_STREAM frame MUST set - the FLAG_FIN, indicating that the client intends to send no further - data on this stream. For requests which do contain a body, the - SYN_STREAM will not contain the FLAG_FIN, and the body will follow - the SYN_STREAM in a series of DATA frames. The last DATA frame will - set the FLAG_FIN to indicate the end of the body. - - The SYN_STREAM Name/Value section will contain all of the HTTP - headers which are associated with an HTTP request. The header block - in SPDY is mostly unchanged from today's HTTP header block, with the - following differences: - - The first line of the request is unfolded into name/value pairs - like other HTTP headers and MUST be present: - - ":method" - the HTTP method for this request (e.g. "GET", - "POST", "HEAD", etc) - - ":path" - the url-path for this url with "/" prefixed. (See - RFC1738 [RFC1738]). For example, for - "http://www.google.com/search?q=dogs" the path would be - "/search?q=dogs". - - ":version" - the HTTP version of this request (e.g. - "HTTP/1.1") - - In addition, the following two name/value pairs must also be - present in every request: - - ":host" - the hostport (See RFC1738 [RFC1738]) portion of the - URL for this request (e.g. "www.google.com:1234"). This header - is the same as the HTTP 'Host' header. - - ":scheme" - the scheme portion of the URL for this request - (e.g. "https")) - - Header names are all lowercase. - - The Connection, Host, Keep-Alive, Proxy-Connection, and Transfer- - Encoding headers are not valid and MUST not be sent. - - - -Belshe & Peon Expires August 4, 2012 [Page 34] - -Internet-Draft SPDY Feb 2012 - - - User-agents MUST support gzip compression. Regardless of the - Accept-Encoding sent by the user-agent, the server may always send - content encoded with gzip or deflate encoding. - - If a server receives a request where the sum of the data frame - payload lengths does not equal the size of the Content-Length - header, the server MUST return a 400 (Bad Request) error. - - POST-specific changes: - - Although POSTs are inherently chunked, POST requests SHOULD - also be accompanied by a Content-Length header. There are two - reasons for this: First, it assists with upload progress meters - for an improved user experience. But second, we know from - early versions of SPDY that failure to send a content length - header is incompatible with many existing HTTP server - implementations. Existing user-agents do not omit the Content- - Length header, and server implementations have come to depend - upon this. - - The user-agent is free to prioritize requests as it sees fit. If the - user-agent cannot make progress without receiving a resource, it - should attempt to raise the priority of that resource. Resources - such as images, SHOULD generally use the lowest priority. - - If a client sends a SYN_STREAM without all of the method, host, path, - scheme, and version headers, the server MUST reply with a HTTP 400 - Bad Request reply. - -3.2.2. Response - - The server responds to a client request with a SYN_REPLY frame. - Symmetric to the client's upload stream, server will send data after - the SYN_REPLY frame via a series of DATA frames, and the last data - frame will contain the FLAG_FIN to indicate successful end-of-stream. - If a response (like a 202 or 204 response) contains no body, the - SYN_REPLY frame may contain the FLAG_FIN flag to indicate no further - data will be sent on the stream. - - The response status line is unfolded into name/value pairs like - other HTTP headers and must be present: - - ":status" - The HTTP response status code (e.g. "200" or "200 - OK") - - ":version" - The HTTP response version (e.g. "HTTP/1.1") - - - - - -Belshe & Peon Expires August 4, 2012 [Page 35] - -Internet-Draft SPDY Feb 2012 - - - All header names must be lowercase. - - The Connection, Keep-Alive, Proxy-Connection, and Transfer- - Encoding headers are not valid and MUST not be sent. - - Responses MAY be accompanied by a Content-Length header for - advisory purposes. (e.g. for UI progress meters) - - If a client receives a response where the sum of the data frame - payload lengths does not equal the size of the Content-Length - header, the client MUST ignore the content length header. - - If a client receives a SYN_REPLY without a status or without a - version header, the client must reply with a RST_STREAM frame - indicating a PROTOCOL ERROR. - -3.2.3. Authentication - - When a client sends a request to an origin server that requires - authentication, the server can reply with a "401 Unauthorized" - response, and include a WWW-Authenticate challenge header that - defines the authentication scheme to be used. The client then - retries the request with an Authorization header appropriate to the - specified authentication scheme. - - There are four options for proxy authentication, Basic, Digest, NTLM - and Negotiate (SPNEGO). The first two options were defined in - RFC2617 [RFC2617], and are stateless. The second two options were - developed by Microsoft and specified in RFC4559 [RFC4559], and are - stateful; otherwise known as multi-round authentication, or - connection authentication. - -3.2.3.1. Stateless Authentication - - Stateless Authentication over SPDY is identical to how it is - performed over HTTP. If multiple SPDY streams are concurrently sent - to a single server, each will authenticate independently, similar to - how two HTTP connections would independently authenticate to a proxy - server. - -3.2.3.2. Stateful Authentication - - Unfortunately, the stateful authentication mechanisms were - implemented and defined in a such a way that directly violates - RFC2617 - they do not include a "realm" as part of the request. This - is problematic in SPDY because it makes it impossible for a client to - disambiguate two concurrent server authentication challenges. - - - - -Belshe & Peon Expires August 4, 2012 [Page 36] - -Internet-Draft SPDY Feb 2012 - - - To deal with this case, SPDY servers using Stateful Authentication - MUST implement one of two changes: - - Servers can add a "realm=<desired realm>" header so that the two - authentication requests can be disambiguated and run concurrently. - Unfortunately, given how these mechanisms work, this is probably - not practical. - - Upon sending the first stateful challenge response, the server - MUST buffer and defer all further frames which are not part of - completing the challenge until the challenge has completed. - Completing the authentication challenge may take multiple round - trips. Once the client receives a "401 Authenticate" response for - a stateful authentication type, it MUST stop sending new requests - to the server until the authentication has completed by receiving - a non-401 response on at least one stream. - -3.3. Server Push Transactions - - SPDY enables a server to send multiple replies to a client for a - single request. The rationale for this feature is that sometimes a - server knows that it will need to send multiple resources in response - to a single request. Without server push features, the client must - first download the primary resource, then discover the secondary - resource(s), and request them. Pushing of resources avoids the - round-trip delay, but also creates a potential race where a server - can be pushing content which a user-agent is in the process of - requesting. The following mechanics attempt to prevent the race - condition while enabling the performance benefit. - - Browsers receiving a pushed response MUST validate that the server is - authorized to push the URL using the browser same-origin [RFC6454] - policy. For example, a SPDY connection to www.foo.com is generally - not permitted to push a response for www.evil.com. - - If the browser accepts a pushed response (e.g. it does not send a - RST_STREAM), the browser MUST attempt to cache the pushed response in - same way that it would cache any other response. This means - validating the response headers and inserting into the disk cache. - - Because pushed responses have no request, they have no request - headers associated with them. At the framing layer, SPDY pushed - streams contain an "associated-stream-id" which indicates the - requested stream for which the pushed stream is related. The pushed - stream inherits all of the headers from the associated-stream-id with - the exception of ":host", ":scheme", and ":path", which are provided - as part of the pushed response stream headers. The browser MUST - store these inherited and implied request headers with the cached - - - -Belshe & Peon Expires August 4, 2012 [Page 37] - -Internet-Draft SPDY Feb 2012 - - - resource. - - Implementation note: With server push, it is theoretically possible - for servers to push unreasonable amounts of content or resources to - the user-agent. Browsers MUST implement throttles to protect against - unreasonable push attacks. - -3.3.1. Server implementation - - When the server intends to push a resource to the user-agent, it - opens a new stream by sending a unidirectional SYN_STREAM. The - SYN_STREAM MUST include an Associated-To-Stream-ID, and MUST set the - FLAG_UNIDIRECTIONAL flag. The SYN_STREAM MUST include headers for - ":scheme", ":host", ":path", which represent the URL for the resource - being pushed. Subsequent headers may follow in HEADERS frames. The - purpose of the association is so that the user-agent can - differentiate which request induced the pushed stream; without it, if - the user-agent had two tabs open to the same page, each pushing - unique content under a fixed URL, the user-agent would not be able to - differentiate the requests. - - The Associated-To-Stream-ID must be the ID of an existing, open - stream. The reason for this restriction is to have a clear endpoint - for pushed content. If the user-agent requested a resource on stream - 11, the server replies on stream 11. It can push any number of - additional streams to the client before sending a FLAG_FIN on stream - 11. However, once the originating stream is closed no further push - streams may be associated with it. The pushed streams do not need to - be closed (FIN set) before the originating stream is closed, they - only need to be created before the originating stream closes. - - It is illegal for a server to push a resource with the Associated-To- - Stream-ID of 0. - - To minimize race conditions with the client, the SYN_STREAM for the - pushed resources MUST be sent prior to sending any content which - could allow the client to discover the pushed resource and request - it. - - The server MUST only push resources which would have been returned - from a GET request. - - Note: If the server does not have all of the Name/Value Response - headers available at the time it issues the HEADERS frame for the - pushed resource, it may later use an additional HEADERS frame to - augment the name/value pairs to be associated with the pushed stream. - The subsequent HEADERS frame(s) must not contain a header for - ':host', ':scheme', or ':path' (e.g. the server can't change the - - - -Belshe & Peon Expires August 4, 2012 [Page 38] - -Internet-Draft SPDY Feb 2012 - - - identity of the resource to be pushed). The HEADERS frame must not - contain duplicate headers with a previously sent HEADERS frame. The - server must send a HEADERS frame including the scheme/host/port - headers before sending any data frames on the stream. - -3.3.2. Client implementation - - When fetching a resource the client has 3 possibilities: - - the resource is not being pushed - - the resource is being pushed, but the data has not yet arrived - - the resource is being pushed, and the data has started to arrive - - When a SYN_STREAM and HEADERS frame which contains an Associated-To- - Stream-ID is received, the client must not issue GET requests for the - resource in the pushed stream, and instead wait for the pushed stream - to arrive. - - If a client receives a server push stream with stream-id 0, it MUST - issue a session error (Section 2.4.1) with the status code - PROTOCOL_ERROR. - - When a client receives a SYN_STREAM from the server without a the - ':host', ':scheme', and ':path' headers in the Name/Value section, it - MUST reply with a RST_STREAM with error code HTTP_PROTOCOL_ERROR. - - To cancel individual server push streams, the client can issue a - stream error (Section 2.4.2) with error code CANCEL. Upon receipt, - the server MUST stop sending on this stream immediately (this is an - Abrupt termination). - - To cancel all server push streams related to a request, the client - may issue a stream error (Section 2.4.2) with error code CANCEL on - the associated-stream-id. By cancelling that stream, the server MUST - immediately stop sending frames for any streams with - in-association-to for the original stream. - - If the server sends a HEADER frame containing duplicate headers with - a previous HEADERS frame for the same stream, the client must issue a - stream error (Section 2.4.2) with error code PROTOCOL ERROR. - - If the server sends a HEADERS frame after sending a data frame for - the same stream, the client MAY ignore the HEADERS frame. Ignoring - the HEADERS frame after a data frame prevents handling of HTTP's - trailing headers - (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.40). - - - -Belshe & Peon Expires August 4, 2012 [Page 39] - -Internet-Draft SPDY Feb 2012 - - -4. Design Rationale and Notes - - Authors' notes: The notes in this section have no bearing on the SPDY - protocol as specified within this document, and none of these notes - should be considered authoritative about how the protocol works. - However, these notes may prove useful in future debates about how to - resolve protocol ambiguities or how to evolve the protocol going - forward. They may be removed before the final draft. - -4.1. Separation of Framing Layer and Application Layer - - Readers may note that this specification sometimes blends the framing - layer (Section 2) with requirements of a specific application - HTTP - (Section 3). This is reflected in the request/response nature of the - streams, the definition of the HEADERS and compression contexts which - are very similar to HTTP, and other areas as well. - - This blending is intentional - the primary goal of this protocol is - to create a low-latency protocol for use with HTTP. Isolating the - two layers is convenient for description of the protocol and how it - relates to existing HTTP implementations. However, the ability to - reuse the SPDY framing layer is a non goal. - -4.2. Error handling - Framing Layer - - Error handling at the SPDY layer splits errors into two groups: Those - that affect an individual SPDY stream, and those that do not. - - When an error is confined to a single stream, but general framing is - in tact, SPDY attempts to use the RST_STREAM as a mechanism to - invalidate the stream but move forward without aborting the - connection altogether. - - For errors occuring outside of a single stream context, SPDY assumes - the entire session is hosed. In this case, the endpoint detecting - the error should initiate a connection close. - -4.3. One Connection Per Domain - - SPDY attempts to use fewer connections than other protocols have - traditionally used. The rationale for this behavior is because it is - very difficult to provide a consistent level of service (e.g. TCP - slow-start), prioritization, or optimal compression when the client - is connecting to the server through multiple channels. - - Through lab measurements, we have seen consistent latency benefits by - using fewer connections from the client. The overall number of - packets sent by SPDY can be as much as 40% less than HTTP. Handling - - - -Belshe & Peon Expires August 4, 2012 [Page 40] - -Internet-Draft SPDY Feb 2012 - - - large numbers of concurrent connections on the server also does - become a scalability problem, and SPDY reduces this load. - - The use of multiple connections is not without benefit, however. - Because SPDY multiplexes multiple, independent streams onto a single - stream, it creates a potential for head-of-line blocking problems at - the transport level. In tests so far, the negative effects of head- - of-line blocking (especially in the presence of packet loss) is - outweighed by the benefits of compression and prioritization. - -4.4. Fixed vs Variable Length Fields - - SPDY favors use of fixed length 32bit fields in cases where smaller, - variable length encodings could have been used. To some, this seems - like a tragic waste of bandwidth. SPDY choses the simple encoding - for speed and simplicity. - - The goal of SPDY is to reduce latency on the network. The overhead - of SPDY frames is generally quite low. Each data frame is only an 8 - byte overhead for a 1452 byte payload (~0.6%). At the time of this - writing, bandwidth is already plentiful, and there is a strong trend - indicating that bandwidth will continue to increase. With an average - worldwide bandwidth of 1Mbps, and assuming that a variable length - encoding could reduce the overhead by 50%, the latency saved by using - a variable length encoding would be less than 100 nanoseconds. More - interesting are the effects when the larger encodings force a packet - boundary, in which case a round-trip could be induced. However, by - addressing other aspects of SPDY and TCP interactions, we believe - this is completely mitigated. - -4.5. Compression Context(s) - - When isolating the compression contexts used for communicating with - multiple origins, we had a few choices to make. We could have - maintained a map (or list) of compression contexts usable for each - origin. The basic case is easy - each HEADERS frame would need to - identify the context to use for that frame. However, compression - contexts are not cheap, so the lifecycle of each context would need - to be bounded. For proxy servers, where we could churn through many - contexts, this would be a concern. We considered using a static set - of contexts, say 16 of them, which would bound the memory use. We - also considered dynamic contexts, which could be created on the fly, - and would need to be subsequently destroyed. All of these are - complicated, and ultimately we decided that such a mechanism creates - too many problems to solve. - - Alternatively, we've chosen the simple approach, which is to simply - provide a flag for resetting the compression context. For the common - - - -Belshe & Peon Expires August 4, 2012 [Page 41] - -Internet-Draft SPDY Feb 2012 - - - case (no proxy), this fine because most requests are to the same - origin and we never need to reset the context. For cases where we - are using two different origins over a single SPDY session, we simply - reset the compression state between each transition. - -4.6. Unidirectional streams - - Many readers notice that unidirectional streams are both a bit - confusing in concept and also somewhat redundant. If the recipient - of a stream doesn't wish to send data on a stream, it could simply - send a SYN_REPLY with the FLAG_FIN bit set. The FLAG_UNIDIRECTIONAL - is, therefore, not necessary. - - It is true that we don't need the UNIDIRECTIONAL markings. It is - added because it avoids the recipient of pushed streams from needing - to send a set of empty frames (e.g. the SYN_STREAM w/ FLAG_FIN) which - otherwise serve no purpose. - -4.7. Data Compression - - Generic compression of data portion of the streams (as opposed to - compression of the headers) without knowing the content of the stream - is redundant. There is no value in compressing a stream which is - already compressed. Because of this, SPDY does allow data - compression to be optional. We included it because study of existing - websites shows that many sites are not using compression as they - should, and users suffer because of it. We wanted a mechanism where, - at the SPDY layer, site administrators could simply force compression - - it is better to compress twice than to not compress. - - Overall, however, with this feature being optional and sometimes - redundant, it is unclear if it is useful at all. We will likely - remove it from the specification. - -4.8. Server Push - - A subtle but important point is that server push streams must be - declared before the associated stream is closed. The reason for this - is so that proxies have a lifetime for which they can discard - information about previous streams. If a pushed stream could - associate itself with an already-closed stream, then endpoints would - not have a specific lifecycle for when they could disavow knowledge - of the streams which went before. - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 42] - -Internet-Draft SPDY Feb 2012 - - -5. Security Considerations - -5.1. Use of Same-origin constraints - - This specification uses the same-origin policy [RFC6454] in all cases - where verification of content is required. - -5.2. HTTP Headers and SPDY Headers - - At the application level, HTTP uses name/value pairs in its headers. - Because SPDY merges the existing HTTP headers with SPDY headers, - there is a possibility that some HTTP applications already use a - particular header name. To avoid any conflicts, all headers - introduced for layering HTTP over SPDY are prefixed with ":". ":" is - not a valid sequence in HTTP header naming, preventing any possible - conflict. - -5.3. Cross-Protocol Attacks - - By utilizing TLS, we believe that SPDY introduces no new cross- - protocol attacks. TLS encrypts the contents of all transmission - (except the handshake itself), making it difficult for attackers to - control the data which could be used in a cross-protocol attack. - -5.4. Server Push Implicit Headers - - Pushed resources do not have an associated request. In order for - existing HTTP cache control validations (such as the Vary header) to - work, however, all cached resources must have a set of request - headers. For this reason, browsers MUST be careful to inherit - request headers from the associated stream for the push. This - includes the 'Cookie' header. - - - - - - - - - - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 43] - -Internet-Draft SPDY Feb 2012 - - -6. Privacy Considerations - -6.1. Long Lived Connections - - SPDY aims to keep connections open longer between clients and servers - in order to reduce the latency when a user makes a request. The - maintenance of these connections over time could be used to expose - private information. For example, a user using a browser hours after - the previous user stopped using that browser may be able to learn - about what the previous user was doing. This is a problem with HTTP - in its current form as well, however the short lived connections make - it less of a risk. - -6.2. SETTINGS frame - - The SPDY SETTINGS frame allows servers to store out-of-band - transmitted information about the communication between client and - server on the client. Although this is intended only to be used to - reduce latency, renegade servers could use it as a mechanism to store - identifying information about the client in future requests. - - Clients implementing privacy modes, such as Google Chrome's - "incognito mode", may wish to disable client-persisted SETTINGS - storage. - - Clients MUST clear persisted SETTINGS information when clearing the - cookies. - - TODO: Put range maximums on each type of setting to limit - inappropriate uses. - - - - - - - - - - - - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 44] - -Internet-Draft SPDY Feb 2012 - - -7. Incompatibilities with SPDY draft #2 - - Here is a list of the major changes between this draft and draft #2. - - Addition of flow control - - Increased 16 bit length fields in SYN_STREAM and SYN_REPLY to 32 - bits. - - Changed definition of compression for DATA frames - - Updated compression dictionary - - Fixed off-by-one on the compression dictionary for headers - - Increased priority field from 2bits to 3bits. - - Removed NOOP frame - - Split the request "url" into "scheme", "host", and "path" - - Added the requirement that POSTs contain content-length. - - Removed wasted 16bits of unused space from the end of the - SYN_REPLY and HEADERS frames. - - Fixed bug: Priorities were described backward (0 was lowest - instead of highest). - - Fixed bug: Name/Value header counts were duplicated in both the - Name Value header block and also the containing frame. - - - - - - - - - - - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 45] - -Internet-Draft SPDY Feb 2012 - - -8. Requirements Notation - - The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", - "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this - document are to be interpreted as described in RFC 2119 [RFC2119]. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 46] - -Internet-Draft SPDY Feb 2012 - - -9. Acknowledgements - - Many individuals have contributed to the design and evolution of - SPDY: Adam Langley, Wan-Teh Chang, Jim Morrison, Mark Nottingham, - Alyssa Wilk, Costin Manolache, William Chan, Vitaliy Lvin, Joe Chan, - Adam Barth, Ryan Hamilton, Gavin Peters, Kent Alstad, Kevin Lindsay, - Paul Amer, Fan Yang, Jonathan Leighton - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 47] - -Internet-Draft SPDY Feb 2012 - - -10. Normative References - - [RFC0793] Postel, J., "Transmission Control Protocol", STD 7, - RFC 793, September 1981. - - [RFC1738] Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform - Resource Locators (URL)", RFC 1738, December 1994. - - [RFC1950] Deutsch, L. and J-L. Gailly, "ZLIB Compressed Data Format - Specification version 3.3", RFC 1950, May 1996. - - [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate - Requirement Levels", BCP 14, RFC 2119, March 1997. - - [RFC2285] Mandeville, R., "Benchmarking Terminology for LAN - Switching Devices", RFC 2285, February 1998. - - [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., - Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext - Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. - - [RFC2617] Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., - Leach, P., Luotonen, A., and L. Stewart, "HTTP - Authentication: Basic and Digest Access Authentication", - RFC 2617, June 1999. - - [RFC4559] Jaganathan, K., Zhu, L., and J. Brezak, "SPNEGO-based - Kerberos and NTLM HTTP Authentication in Microsoft - Windows", RFC 4559, June 2006. - - [RFC4366] Blake-Wilson, S., Nystrom, M., Hopwood, D., Mikkelsen, J., - and T. Wright, "Transport Layer Security (TLS) - Extensions", RFC 4366, April 2006. - - [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security - (TLS) Protocol Version 1.2", RFC 5246, August 2008. - - [RFC6454] Barth, A., "The Web Origin Concept", RFC 6454, - December 2011. - - [TLSNPN] Langley, A., "TLS Next Protocol Negotiation", - <http://tools.ietf.org/html/ - draft-agl-tls-nextprotoneg-01>. - - [ASCII] "US-ASCII. Coded Character Set - 7-Bit American Standard - Code for Information Interchange. Standard ANSI X3.4-1986, - ANSI, 1986.". - - - - -Belshe & Peon Expires August 4, 2012 [Page 48] - -Internet-Draft SPDY Feb 2012 - - - [UDELCOMPRESSION] - Yang, F., Amer, P., and J. Leighton, "A Methodology to - Derive SPDY's Initial Dictionary for Zlib Compression", - <http://www.eecis.udel.edu/~amer/PEL/poc/pdf/ - SPDY-Fan.pdf>. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 49] - -Internet-Draft SPDY Feb 2012 - - -Appendix A. Changes - - To be removed by RFC Editor before publication - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 50] - -Internet-Draft SPDY Feb 2012 - - -Authors' Addresses - - Mike Belshe - Twist - - Email: mbelshe@chromium.org - - - Roberto Peon - Google, Inc - - Email: fenix@google.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Belshe & Peon Expires August 4, 2012 [Page 51] - diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am @@ -13,17 +13,6 @@ if USE_COVERAGE AM_CFLAGS += --coverage endif -if ENABLE_SPDY -spdyex = \ - spdy_event_loop \ - spdy_fileserver \ - spdy_response_with_callback - -if HAVE_SPDYLAY -spdyex += mhd2spdy -endif -endif - # example programs noinst_PROGRAMS = \ @@ -37,8 +26,7 @@ noinst_PROGRAMS = \ fileserver_example \ fileserver_example_dirs \ fileserver_example_external_select \ - refuse_post_example \ - $(spdyex) + refuse_post_example if ENABLE_HTTPS @@ -98,15 +86,6 @@ demo_https_LDADD = \ $(top_builddir)/src/microhttpd/libmicrohttpd.la \ $(PTHREAD_LIBS) -lmagic -mhd2spdy_SOURCES = \ - mhd2spdy.c \ - mhd2spdy_spdy.c mhd2spdy_spdy.h \ - mhd2spdy_http.c mhd2spdy_http.h \ - mhd2spdy_structures.c mhd2spdy_structures.h -mhd2spdy_LDADD = \ - $(top_builddir)/src/microhttpd/libmicrohttpd.la \ - -lssl -lcrypto -lspdylay - benchmark_SOURCES = \ benchmark.c benchmark_CPPFLAGS = \ @@ -179,20 +158,3 @@ https_fileserver_example_LDADD = \ $(top_builddir)/src/microhttpd/libmicrohttpd.la -spdy_event_loop_SOURCES = \ - spdy_event_loop.c -spdy_event_loop_LDADD = \ - $(top_builddir)/src/microspdy/libmicrospdy.la \ - -lz - -spdy_fileserver_SOURCES = \ - spdy_fileserver.c -spdy_fileserver_LDADD = \ - $(top_builddir)/src/microspdy/libmicrospdy.la \ - -lz - -spdy_response_with_callback_SOURCES = \ - spdy_response_with_callback.c -spdy_response_with_callback_LDADD = \ - $(top_builddir)/src/microspdy/libmicrospdy.la \ - -lz diff --git a/src/examples/mhd2spdy.c b/src/examples/mhd2spdy.c @@ -1,322 +0,0 @@ -/* - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file mhd2spdy.c - * @brief The main file of the HTTP-to-SPDY proxy with the 'main' function - * and event loop. No threads are used. - * Currently only GET is supported. - * TODOs: - * - non blocking SSL connect - * - check certificate - * - on closing spdy session, close sockets for all requests - * @author Andrey Uzunov - */ - - -#include "mhd2spdy_structures.h" -#include "mhd2spdy_spdy.h" -#include "mhd2spdy_http.h" - - -static int run = 1; -//static int spdy_close = 0; - - -static void -catch_signal(int signal) -{ - (void)signal; - //spdy_close = 1; - run = 0; -} - - -void -print_stat() -{ - if(!glob_opt.statistics) - return; - - printf("--------------------------\n"); - printf("Statistics (TLS overhead is ignored when used):\n"); - //printf("HTTP bytes received: %lld\n", glob_stat.http_bytes_received); - //printf("HTTP bytes sent: %lld\n", glob_stat.http_bytes_sent); - printf("SPDY bytes sent: %lld\n", glob_stat.spdy_bytes_sent); - printf("SPDY bytes received: %lld\n", glob_stat.spdy_bytes_received); - printf("SPDY bytes received and dropped: %lld\n", glob_stat.spdy_bytes_received_and_dropped); -} - - -int -run_everything () -{ - unsigned long long timeoutlong=0; - struct timeval timeout; - int ret; - fd_set rs; - fd_set ws; - fd_set es; - int maxfd = -1; - int maxfd_s = -1; - struct MHD_Daemon *daemon; - nfds_t spdy_npollfds = 1; - struct URI * spdy2http_uri = NULL; - struct SPDY_Connection *connection; - struct SPDY_Connection *connections[MAX_SPDY_CONNECTIONS]; - struct SPDY_Connection *connection_for_delete; - - if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) - PRINT_INFO("signal failed"); - - if (signal(SIGINT, catch_signal) == SIG_ERR) - PRINT_INFO("signal failed"); - - glob_opt.streams_opened = 0; - glob_opt.responses_pending = 0; - //glob_opt.global_memory = 0; - - srand(time(NULL)); - - if(init_parse_uri(&glob_opt.uri_preg)) - DIE("Regexp compilation failed"); - - if(NULL != glob_opt.spdy2http_str) - { - ret = parse_uri(&glob_opt.uri_preg, glob_opt.spdy2http_str, &spdy2http_uri); - if(ret != 0) - DIE("spdy_parse_uri failed"); - } - - SSL_load_error_strings(); - SSL_library_init(); - glob_opt.ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - if(glob_opt.ssl_ctx == NULL) { - PRINT_INFO2("SSL_CTX_new %s", ERR_error_string(ERR_get_error(), NULL)); - abort(); - } - spdy_ssl_init_ssl_ctx(glob_opt.ssl_ctx, &glob_opt.spdy_proto_version); - - daemon = MHD_start_daemon ( - MHD_SUPPRESS_DATE_NO_CLOCK, - glob_opt.listen_port, - NULL, NULL, &http_cb_request, NULL, - MHD_OPTION_URI_LOG_CALLBACK, &http_cb_log, NULL, - MHD_OPTION_NOTIFY_COMPLETED, &http_cb_request_completed, NULL, - MHD_OPTION_END); - if(NULL==daemon) - DIE("MHD_start_daemon failed"); - - do - { - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - if(NULL == glob_opt.spdy_connection && NULL != glob_opt.spdy2http_str) - { - glob_opt.spdy_connection = spdy_connect(spdy2http_uri, spdy2http_uri->port, strcmp("https", spdy2http_uri->scheme)==0); - if(NULL == glob_opt.spdy_connection && glob_opt.only_proxy) - PRINT_INFO("cannot connect to the proxy"); - } - - FD_ZERO(&rs); - FD_ZERO(&ws); - FD_ZERO(&es); - - ret = MHD_get_timeout(daemon, &timeoutlong); - if(MHD_NO == ret || timeoutlong > 5000) - timeout.tv_sec = 5; - else - { - timeout.tv_sec = timeoutlong / 1000; - timeout.tv_usec = (timeoutlong % 1000) * 1000; - } - - if(MHD_NO == MHD_get_fdset (daemon, - &rs, - &ws, - &es, - &maxfd)) - { - PRINT_INFO("MHD_get_fdset error"); - } - assert(-1 != maxfd); - - maxfd_s = spdy_get_selectfdset( - &rs, - &ws, - &es, - connections, MAX_SPDY_CONNECTIONS, &spdy_npollfds); - if(maxfd_s > maxfd) - maxfd = maxfd_s; - - PRINT_INFO2("MHD timeout %lld %lld", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec); - - glob_opt.spdy_data_received = false; - - ret = select(maxfd+1, &rs, &ws, &es, &timeout); - PRINT_INFO2("timeout now %lld %lld ret is %i", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec, ret); - - switch(ret) - { - case -1: - PRINT_INFO2("select error: %i", errno); - break; - case 0: - //break; - default: - PRINT_INFO("run"); - //MHD_run_from_select(daemon,&rs, &ws, &es); //not closing FDs at some time in past - MHD_run(daemon); - spdy_run_select(&rs, &ws, &es, connections, spdy_npollfds); - if(glob_opt.spdy_data_received) - { - PRINT_INFO("MHD run again"); - //MHD_run_from_select(daemon,&rs, &ws, &es); //not closing FDs at some time in past - MHD_run(daemon); - } - break; - } - } - while(run); - - MHD_stop_daemon (daemon); - - //TODO SSL_free brakes - spdy_free_connection(glob_opt.spdy_connection); - - connection = glob_opt.spdy_connections_head; - while(NULL != connection) - { - connection_for_delete = connection; - connection = connection_for_delete->next; - glob_opt.streams_opened -= connection_for_delete->streams_opened; - DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection_for_delete); - spdy_free_connection(connection_for_delete); - } - - free_uri(spdy2http_uri); - - deinit_parse_uri(&glob_opt.uri_preg); - - SSL_CTX_free(glob_opt.ssl_ctx); - ERR_free_strings(); - EVP_cleanup(); - - PRINT_INFO2("spdy streams: %i; http requests: %i", glob_opt.streams_opened, glob_opt.responses_pending); - //PRINT_INFO2("memory allocated %zu bytes", glob_opt.global_memory); - - print_stat(); - - return 0; -} - - -void -display_usage() -{ - printf( - "Usage: mhd2spdy [-ovs] [-b <SPDY2HTTP-PROXY>] -p <PORT>\n\n" - "OPTIONS:\n" - " -p, --port Listening port.\n" - " -b, --backend-proxy If set, he proxy will send requests to\n" - " that SPDY server or proxy. Set the address\n" - " in the form 'http://host:port'. Use 'https'\n" - " for SPDY over TLS, or 'http' for plain SPDY\n" - " communication with the backend.\n" - " -o, --only-proxy If set, the proxy will always forward the\n" - " requests to the backend proxy. If not set,\n" - " the proxy will first try to establsh SPDY\n" - " connection to the requested server. If the\n" - " server does not support SPDY and TLS, the\n" - " backend proxy will be used for the request.\n" - " -v, --verbose Print debug information.\n" - " -s, --statistics Print simple statistics on exit.\n\n" - - ); -} - - -int -main (int argc, - char *const *argv) -{ - int getopt_ret; - int option_index; - struct option long_options[] = { - {"port", required_argument, 0, 'p'}, - {"backend-proxy", required_argument, 0, 'b'}, - {"verbose", no_argument, 0, 'v'}, - {"only-proxy", no_argument, 0, 'o'}, - {"statistics", no_argument, 0, 's'}, - {0, 0, 0, 0} - }; - - while (1) - { - getopt_ret = getopt_long( argc, argv, "p:b:vos", long_options, &option_index); - if (getopt_ret == -1) - break; - - switch(getopt_ret) - { - case 'p': - glob_opt.listen_port = atoi(optarg); - break; - - case 'b': - glob_opt.spdy2http_str = strdup(optarg); - if(NULL == glob_opt.spdy2http_str) - return 1; - break; - - case 'v': - glob_opt.verbose = true; - break; - - case 'o': - glob_opt.only_proxy = true; - break; - - case 's': - glob_opt.statistics = true; - break; - - case 0: - PRINT_INFO("0 from getopt"); - break; - - case '?': - display_usage(); - return 1; - - default: - DIE("default from getopt"); - } - } - - if( - 0 == glob_opt.listen_port - || (glob_opt.only_proxy && NULL == glob_opt.spdy2http_str) - ) - { - display_usage(); - return 1; - } - - return run_everything(); -} diff --git a/src/examples/mhd2spdy_http.c b/src/examples/mhd2spdy_http.c @@ -1,422 +0,0 @@ -/* - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file mhd2spdy_http.c - * @brief HTTP part of the proxy. libmicrohttpd is used for the server side. - * @author Andrey Uzunov - */ - -#include "mhd2spdy_structures.h" -#include "mhd2spdy_http.h" -#include "mhd2spdy_spdy.h" - - -void * -http_cb_log(void * cls, -const char * uri) -{ - (void)cls; - - struct HTTP_URI * http_uri; - - PRINT_INFO2("log uri '%s'\n", uri); - - //TODO not freed once in a while - if(NULL == (http_uri = au_malloc(sizeof(struct HTTP_URI )))) - return NULL; - http_uri->uri = strdup(uri); - return http_uri; -} - - -static int -http_cb_iterate(void *cls, - enum MHD_ValueKind kind, - const char *name, - const char *value) -{ - (void)kind; - - static char * const forbidden[] = {"Transfer-Encoding", - "Proxy-Connection", - "Keep-Alive", - "Connection"}; - static int forbidden_size = 4; - int i; - struct SPDY_Headers *spdy_headers = (struct SPDY_Headers *)cls; - - if(0 == strcasecmp(name, "Host")) - spdy_headers->nv[9] = (char *)value; - else - { - for(i=0; i<forbidden_size; ++i) - if(0 == strcasecmp(forbidden[i], name)) - return MHD_YES; - spdy_headers->nv[spdy_headers->cnt++] = (char *)name; - spdy_headers->nv[spdy_headers->cnt++] = (char *)value; - } - - return MHD_YES; -} - - -static ssize_t -http_cb_response (void *cls, - uint64_t pos, - char *buffer, - size_t max) -{ - (void)pos; - - int ret; - struct Proxy *proxy = (struct Proxy *)cls; - void *newbody; - const union MHD_ConnectionInfo *info; - int val = 1; - - PRINT_INFO2("http_cb_response for %s", proxy->url); - - if(proxy->spdy_error) - return MHD_CONTENT_READER_END_WITH_ERROR; - - if(0 == proxy->http_body_size && (proxy->done || !proxy->spdy_active)) - { - PRINT_INFO("sent end of stream"); - return MHD_CONTENT_READER_END_OF_STREAM; - } - - if(!proxy->http_body_size)//nothing to write now - { - //flush data - info = MHD_get_connection_info (proxy->http_connection, - MHD_CONNECTION_INFO_CONNECTION_FD); - ret = setsockopt(info->connect_fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); - if(ret == -1) { - DIE("setsockopt"); - } - - PRINT_INFO("FLUSH data"); - return 0; - } - - if(max >= proxy->http_body_size) - { - ret = proxy->http_body_size; - newbody = NULL; - } - else - { - ret = max; - if(NULL == (newbody = au_malloc(proxy->http_body_size - max))) - { - PRINT_INFO("no memory"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } - memcpy(newbody, proxy->http_body + max, proxy->http_body_size - max); - } - memcpy(buffer, proxy->http_body, ret); - free(proxy->http_body); - proxy->http_body = newbody; - proxy->http_body_size -= ret; - - if(proxy->length >= 0) - { - proxy->length -= ret; - } - - PRINT_INFO2("response_callback, size: %i",ret); - - return ret; -} - - -static void -http_cb_response_done(void *cls) -{ - (void)cls; - //TODO remove -} - -int -http_cb_request (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, - size_t *upload_data_size, - void **ptr) -{ - (void)cls; - (void)url; - (void)upload_data; - (void)upload_data_size; - - int ret; - struct Proxy *proxy; - struct SPDY_Headers spdy_headers; - bool with_body = false; - struct HTTP_URI *http_uri; - const char *header_value; - - if (NULL == ptr || NULL == *ptr) - return MHD_NO; - - http_uri = (struct HTTP_URI *)*ptr; - - if(NULL == http_uri->proxy) - { - //first call for this request - if (0 != strcmp (method, MHD_HTTP_METHOD_GET) && 0 != strcmp (method, MHD_HTTP_METHOD_POST)) - { - free(http_uri->uri); - free(http_uri); - PRINT_INFO2("unexpected method %s", method); - return MHD_NO; - } - - if(NULL == (proxy = au_malloc(sizeof(struct Proxy)))) - { - free(http_uri->uri); - free(http_uri); - PRINT_INFO("No memory"); - return MHD_NO; - } - - ++glob_opt.responses_pending; - proxy->id = rand(); - proxy->http_active = true; - proxy->http_connection = connection; - http_uri->proxy = proxy; - return MHD_YES; - } - - proxy = http_uri->proxy; - - if(proxy->spdy_error || proxy->http_error) - return MHD_NO; // handled at different place TODO? leaks? - - if(proxy->spdy_active) - { - if(0 == strcmp (method, MHD_HTTP_METHOD_POST)) - { - PRINT_INFO("POST processing"); - - int rc= spdylay_session_resume_data(proxy->spdy_connection->session, proxy->stream_id); - PRINT_INFO2("rc is %i stream is %i", rc, proxy->stream_id); - proxy->spdy_connection->want_io |= WANT_WRITE; - - if(0 == *upload_data_size) - { - PRINT_INFO("POST http EOF"); - proxy->receiving_done = true; - return MHD_YES; - } - - if(!copy_buffer(upload_data, *upload_data_size, &proxy->received_body, &proxy->received_body_size)) - { - //TODO handle it better? - PRINT_INFO("not enough memory (malloc/realloc returned NULL)"); - return MHD_NO; - } - - *upload_data_size = 0; - - return MHD_YES; - } - - //already handled - PRINT_INFO("unnecessary call to http_cb_request"); - return MHD_YES; - } - - //second call for this request - - PRINT_INFO2("received request for '%s %s %s'", method, http_uri->uri, version); - - proxy->url = http_uri->uri; - - header_value = MHD_lookup_connection_value(connection, - MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); - - with_body = 0 == strcmp (method, MHD_HTTP_METHOD_POST) - && (NULL == header_value || 0 != strcmp ("0", header_value)); - - PRINT_INFO2("body will be sent %i", with_body); - - ret = parse_uri(&glob_opt.uri_preg, proxy->url, &proxy->uri); - if(ret != 0) - DIE("parse_uri failed"); - proxy->http_uri = http_uri; - - spdy_headers.num = MHD_get_connection_values (connection, - MHD_HEADER_KIND, - NULL, - NULL); - if(NULL == (spdy_headers.nv = au_malloc(((spdy_headers.num + 5) * 2 + 1) * sizeof(char *)))) - DIE("no memory"); - spdy_headers.nv[0] = ":method"; spdy_headers.nv[1] = method; - spdy_headers.nv[2] = ":path"; spdy_headers.nv[3] = proxy->uri->path_and_more; - spdy_headers.nv[4] = ":version"; spdy_headers.nv[5] = (char *)version; - spdy_headers.nv[6] = ":scheme"; spdy_headers.nv[7] = proxy->uri->scheme; - spdy_headers.nv[8] = ":host"; spdy_headers.nv[9] = NULL; - //nv[14] = NULL; - spdy_headers.cnt = 10; - MHD_get_connection_values (connection, - MHD_HEADER_KIND, - &http_cb_iterate, - &spdy_headers); - - spdy_headers.nv[spdy_headers.cnt] = NULL; - if(NULL == spdy_headers.nv[9]) - spdy_headers.nv[9] = proxy->uri->host_and_port; - - if(0 != spdy_request(spdy_headers.nv, proxy, with_body)) - { - free(spdy_headers.nv); - //free_proxy(proxy); - - return MHD_NO; - } - free(spdy_headers.nv); - - proxy->http_response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, - 4096, - &http_cb_response, - proxy, - &http_cb_response_done); - - if (NULL == proxy->http_response) - DIE("no response"); - - if(MHD_NO == MHD_add_response_header (proxy->http_response, - "Proxy-Connection", "keep-alive")) - PRINT_INFO("SPDY_name_value_add failed: "); - if(MHD_NO == MHD_add_response_header (proxy->http_response, - "Connection", "Keep-Alive")) - PRINT_INFO("SPDY_name_value_add failed: "); - if(MHD_NO == MHD_add_response_header (proxy->http_response, - "Keep-Alive", "timeout=5, max=100")) - PRINT_INFO("SPDY_name_value_add failed: "); - - proxy->spdy_active = true; - - return MHD_YES; -} - - -void -http_create_response(struct Proxy* proxy, - char **nv) -{ - size_t i; - - if(!proxy->http_active) - return; - - for(i = 0; nv[i]; i += 2) { - if(0 == strcmp(":status", nv[i])) - { - char tmp[4]; - memcpy(&tmp,nv[i+1],3); - tmp[3]=0; - proxy->status = atoi(tmp); - continue; - } - else if(0 == strcmp(":version", nv[i])) - { - proxy->version = nv[i+1]; - continue; - } - else if(0 == strcmp("content-length", nv[i])) - { - continue; - } - - char *header = *(nv+i); - if(MHD_NO == MHD_add_response_header (proxy->http_response, - header, nv[i+1])) - { - PRINT_INFO2("SPDY_name_value_add failed: '%s' '%s'", header, nv[i+1]); - } - PRINT_INFO2("adding '%s: %s'",header, nv[i+1]); - } - - if(MHD_NO == MHD_queue_response (proxy->http_connection, proxy->status, proxy->http_response)){ - PRINT_INFO("No queue"); - //TODO - //abort(); - proxy->http_error = true; - } - - MHD_destroy_response (proxy->http_response); - proxy->http_response = NULL; -} - -void -http_cb_request_completed (void *cls, - struct MHD_Connection *connection, - void **con_cls, - enum MHD_RequestTerminationCode toe) -{ - (void)cls; - (void)connection; - struct HTTP_URI *http_uri; - struct Proxy *proxy; - - http_uri = (struct HTTP_URI *)*con_cls; - if(NULL == http_uri) - return; - proxy = (struct Proxy *)http_uri->proxy; - assert(NULL != proxy); - - PRINT_INFO2("http_cb_request_completed %i for %s; id %i",toe, http_uri->uri, proxy->id); - - if(NULL != proxy->http_response) - { - MHD_destroy_response (proxy->http_response); - proxy->http_response = NULL; - } - - if(proxy->spdy_active) - { - proxy->http_active = false; - if(MHD_REQUEST_TERMINATED_COMPLETED_OK != toe) - { - proxy->http_error = true; - if(proxy->stream_id > 0 /*&& NULL != proxy->spdy_connection->session*/) - { - //send RST_STREAM_STATUS_CANCEL - PRINT_INFO2("send rst_stream %i %i",proxy->spdy_active, proxy->stream_id ); - spdylay_submit_rst_stream(proxy->spdy_connection->session, proxy->stream_id, 5); - } - /*else - { - DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy); - free_proxy(proxy); - }*/ - } - } - else - { - PRINT_INFO2("proxy free http id %i ", proxy->id); - free_proxy(proxy); - } - - --glob_opt.responses_pending; -} diff --git a/src/examples/mhd2spdy_http.h b/src/examples/mhd2spdy_http.h @@ -1,54 +0,0 @@ -/* - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file mhd2spdy_http.h - * @brief HTTP part of the proxy. libmicrohttpd is used for the server side. - * @author Andrey Uzunov - */ - -#ifndef HTTP_H -#define HTTP_H - -#include "mhd2spdy_structures.h" - - -int -http_cb_request (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, - size_t *upload_data_size, - void **ptr); - - -void * http_cb_log(void * cls, const char * uri); - - -void -http_create_response(struct Proxy* proxy, char **nv); - - -void -http_cb_request_completed (void *cls, - struct MHD_Connection *connection, - void **con_cls, - enum MHD_RequestTerminationCode toe); - -#endif diff --git a/src/examples/mhd2spdy_spdy.c b/src/examples/mhd2spdy_spdy.c @@ -1,1150 +0,0 @@ -/* - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** - * @file mhd2spdy_spdy.c - * @brief SPDY part of the proxy. libspdylay is used for the client side. - * The example spdycli.c from spdylay was used as basis; - * however, multiple changes were made. - * @author Tatsuhiro Tsujikawa - * @author Andrey Uzunov - */ - -#include "mhd2spdy_structures.h" -#include "mhd2spdy_spdy.h" -#include "mhd2spdy_http.h" - - -/* - * Prints error containing the function name |func| and message |msg| - * and exit. - */ -static void -spdy_dief(const char *func, - const char *msg) -{ - fprintf(stderr, "FATAL: %s: %s\n", func, msg); - exit(EXIT_FAILURE); -} - - -/* - * Prints error containing the function name |func| and error code - * |error_code| and exit. - */ -void -spdy_diec(const char *func, - int error_code) -{ - fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code, - spdylay_strerror(error_code)); - exit(EXIT_FAILURE); -} - - -static ssize_t -spdy_cb_data_source_read(spdylay_session *session, int32_t stream_id, uint8_t *buf, size_t length, int *eof, spdylay_data_source *source, void *user_data) -{ - (void)session; - (void)stream_id; - (void)user_data; - - ssize_t ret; - assert(NULL != source); - assert(NULL != source->ptr); - struct Proxy *proxy = (struct Proxy *)(source->ptr); - void *newbody; - - - if(length < 1) - { - PRINT_INFO("spdy_cb_data_source_read: length is 0"); - return 0; - } - - if(!proxy->received_body_size)//nothing to write now - { - if(proxy->receiving_done) - { - PRINT_INFO("POST spdy EOF"); - *eof = 1; - } - PRINT_INFO("POST SPDYLAY_ERR_DEFERRED"); - return SPDYLAY_ERR_DEFERRED;//TODO SPDYLAY_ERR_DEFERRED should be used - } - - if(length >= proxy->received_body_size) - { - ret = proxy->received_body_size; - newbody = NULL; - } - else - { - ret = length; - if(NULL == (newbody = malloc(proxy->received_body_size - length))) - { - PRINT_INFO("no memory"); - return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE; - } - memcpy(newbody, proxy->received_body + length, proxy->received_body_size - length); - } - memcpy(buf, proxy->received_body, ret); - free(proxy->received_body); - proxy->received_body = newbody; - proxy->received_body_size -= ret; - - if(0 == proxy->received_body_size && proxy->receiving_done) - { - PRINT_INFO("POST spdy EOF"); - *eof = 1; - } - - PRINT_INFO2("given POST bytes to spdylay: %zd", ret); - - return ret; -} - - -/* - * The implementation of spdylay_send_callback type. Here we write - * |data| with size |length| to the network and return the number of - * bytes actually written. See the documentation of - * spdylay_send_callback for the details. - */ -static ssize_t -spdy_cb_send(spdylay_session *session, - const uint8_t *data, - size_t length, - int flags, - void *user_data) -{ - (void)session; - (void)flags; - - //PRINT_INFO("spdy_cb_send called"); - struct SPDY_Connection *connection; - ssize_t rv; - connection = (struct SPDY_Connection*)user_data; - connection->want_io = IO_NONE; - - if(glob_opt.ignore_rst_stream - && 16 == length - && 0x80 == data[0] - && 0x00 == data[2] - && 0x03 == data[3] - ) - { - PRINT_INFO2("ignoring RST_STREAM for stream_id %i %i %i %i", data[8], data[9], data[10], data[11]); - glob_opt.ignore_rst_stream = false; - return 16; - } - glob_opt.ignore_rst_stream = false; - - if(connection->is_tls) - { - ERR_clear_error(); - rv = SSL_write(connection->ssl, data, length); - if(rv < 0) { - int err = SSL_get_error(connection->ssl, rv); - if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { - connection->want_io |= (err == SSL_ERROR_WANT_READ ? - WANT_READ : WANT_WRITE); - rv = SPDYLAY_ERR_WOULDBLOCK; - } else { - rv = SPDYLAY_ERR_CALLBACK_FAILURE; - } - } - } - else - { - rv = write(connection->fd, - data, - length); - - if (rv < 0) - { - switch(errno) - { - case EAGAIN: - #if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: - #endif - connection->want_io |= WANT_WRITE; - rv = SPDYLAY_ERR_WOULDBLOCK; - break; - - default: - rv = SPDYLAY_ERR_CALLBACK_FAILURE; - } - } - } - - PRINT_INFO2("%zd bytes written by spdy", rv); - - if(rv > 0) - UPDATE_STAT(glob_stat.spdy_bytes_sent, rv); - - return rv; -} - - -/* - * The implementation of spdylay_recv_callback type. Here we read data - * from the network and write them in |buf|. The capacity of |buf| is - * |length| bytes. Returns the number of bytes stored in |buf|. See - * the documentation of spdylay_recv_callback for the details. - */ -static ssize_t -spdy_cb_recv(spdylay_session *session, - uint8_t *buf, - size_t length, - int flags, - void *user_data) -{ - (void)session; - (void)flags; - - struct SPDY_Connection *connection; - ssize_t rv; - - connection = (struct SPDY_Connection*)user_data; - //prevent monopolizing everything - if(!(++connection->counter % 10)) return SPDYLAY_ERR_WOULDBLOCK; - connection->want_io = IO_NONE; - if(connection->is_tls) - { - ERR_clear_error(); - rv = SSL_read(connection->ssl, buf, length); - if(rv < 0) { - int err = SSL_get_error(connection->ssl, rv); - if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { - connection->want_io |= (err == SSL_ERROR_WANT_READ ? - WANT_READ : WANT_WRITE); - rv = SPDYLAY_ERR_WOULDBLOCK; - } else { - rv = SPDYLAY_ERR_CALLBACK_FAILURE; - } - } else if(rv == 0) { - rv = SPDYLAY_ERR_EOF; - } - } - else - { - rv = read(connection->fd, - buf, - length); - - if (rv < 0) - { - switch(errno) - { - case EAGAIN: - #if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: - #endif - connection->want_io |= WANT_READ; - rv = SPDYLAY_ERR_WOULDBLOCK; - break; - - default: - rv = SPDYLAY_ERR_CALLBACK_FAILURE; - } - } - else if(rv == 0) - rv = SPDYLAY_ERR_EOF; - } - - if(rv > 0) - UPDATE_STAT(glob_stat.spdy_bytes_received, rv); - - return rv; -} - - -static void -spdy_cb_before_ctrl_send(spdylay_session *session, - spdylay_frame_type type, - spdylay_frame *frame, - void *user_data) -{ - (void)user_data; - - int32_t stream_id; - struct Proxy *proxy; - - switch(type) { - case SPDYLAY_SYN_STREAM: - stream_id = frame->syn_stream.stream_id; - proxy = spdylay_session_get_stream_user_data(session, stream_id); - proxy->stream_id = stream_id; - ++glob_opt.streams_opened; - ++proxy->spdy_connection->streams_opened; - PRINT_INFO2("opening stream: str open %i; %s", glob_opt.streams_opened, proxy->url); - break; - case SPDYLAY_RST_STREAM: - //try to ignore duplicate RST_STREAMs - //TODO this will ignore RST_STREAMs also for bogus data - glob_opt.ignore_rst_stream = NULL==spdylay_session_get_stream_user_data(session, frame->rst_stream.stream_id); - PRINT_INFO2("sending RST_STREAM for %i; ignore %i; status %i", - frame->rst_stream.stream_id, - glob_opt.ignore_rst_stream, - frame->rst_stream.status_code); - break; - default: - break; - } -} - - -void -spdy_cb_on_ctrl_recv(spdylay_session *session, - spdylay_frame_type type, - spdylay_frame *frame, - void *user_data) -{ - (void)user_data; - - char **nv; - int32_t stream_id; - struct Proxy * proxy; - - switch(type) { - case SPDYLAY_SYN_REPLY: - nv = frame->syn_reply.nv; - stream_id = frame->syn_reply.stream_id; - break; - case SPDYLAY_RST_STREAM: - stream_id = frame->rst_stream.stream_id; - break; - case SPDYLAY_HEADERS: - nv = frame->headers.nv; - stream_id = frame->headers.stream_id; - break; - default: - return; - break; - } - - proxy = spdylay_session_get_stream_user_data(session, stream_id); - if(NULL == proxy) - { - PRINT_INFO2("received frame type %i for unkonwn stream id %i", type, stream_id); - return; - //DIE("no proxy obj"); - } - - switch(type) { - case SPDYLAY_SYN_REPLY: - PRINT_INFO2("received headers for %s", proxy->url); - http_create_response(proxy, nv); - break; - case SPDYLAY_RST_STREAM: - PRINT_INFO2("received reset stream for %s", proxy->url); - proxy->spdy_error = true; - break; - case SPDYLAY_HEADERS: - PRINT_INFO2("received headers for %s", proxy->url); - http_create_response(proxy, nv); - break; - default: - return; - break; - } - - glob_opt.spdy_data_received = true; -} - - -/* - * The implementation of spdylay_on_stream_close_callback type. We use - * this function to know the response is fully received. Since we just - * fetch 1 resource in this program, after reception of the response, - * we submit GOAWAY and close the session. - */ -static void -spdy_cb_on_stream_close(spdylay_session *session, - int32_t stream_id, - spdylay_status_code status_code, - void *user_data) -{ - (void)status_code; - (void)user_data; - - struct Proxy * proxy = spdylay_session_get_stream_user_data(session, stream_id); - - assert(NULL != proxy); - - --glob_opt.streams_opened; - --proxy->spdy_connection->streams_opened; - PRINT_INFO2("closing stream: str opened %i; remove proxy %i", glob_opt.streams_opened, proxy->id); - - DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy); - if(proxy->http_active) - { - proxy->spdy_active = false; - } - else - { - free_proxy(proxy); - } -} - - -/* - * The implementation of spdylay_on_data_chunk_recv_callback type. We - * use this function to print the received response body. - */ -static void -spdy_cb_on_data_chunk_recv(spdylay_session *session, - uint8_t flags, - int32_t stream_id, - const uint8_t *data, - size_t len, - void *user_data) -{ - (void)flags; - (void)user_data; - - struct Proxy *proxy; - proxy = spdylay_session_get_stream_user_data(session, stream_id); - - if(NULL == proxy) - { - PRINT_INFO("proxy in spdy_cb_on_data_chunk_recv is NULL)"); - return; - } - - if(!copy_buffer(data, len, &proxy->http_body, &proxy->http_body_size)) - { - //TODO handle it better? - PRINT_INFO("not enough memory (malloc/realloc returned NULL)"); - return; - } - /* - if(NULL == proxy->http_body) - proxy->http_body = au_malloc(len); - else - proxy->http_body = realloc(proxy->http_body, proxy->http_body_size + len); - if(NULL == proxy->http_body) - { - PRINT_INFO("not enough memory (realloc returned NULL)"); - return ; - } - - memcpy(proxy->http_body + proxy->http_body_size, data, len); - proxy->http_body_size += len; - */ - PRINT_INFO2("received data for %s; %zu bytes", proxy->url, len); - glob_opt.spdy_data_received = true; -} - - -static void -spdy_cb_on_data_recv(spdylay_session *session, - uint8_t flags, - int32_t stream_id, - int32_t length, - void *user_data) -{ - (void)length; - (void)user_data; - - if(flags & SPDYLAY_DATA_FLAG_FIN) - { - struct Proxy *proxy; - proxy = spdylay_session_get_stream_user_data(session, stream_id); - proxy->done = true; - PRINT_INFO2("last data frame received for %s", proxy->url); - } -} - - -/* - * Setup callback functions. Spdylay API offers many callback - * functions, but most of them are optional. The send_callback is - * always required. Since we use spdylay_session_recv(), the - * recv_callback is also required. - */ -static void -spdy_setup_spdylay_callbacks(spdylay_session_callbacks *callbacks) -{ - memset(callbacks, 0, sizeof(spdylay_session_callbacks)); - callbacks->send_callback = spdy_cb_send; - callbacks->recv_callback = spdy_cb_recv; - callbacks->before_ctrl_send_callback = spdy_cb_before_ctrl_send; - callbacks->on_ctrl_recv_callback = spdy_cb_on_ctrl_recv; - callbacks->on_stream_close_callback = spdy_cb_on_stream_close; - callbacks->on_data_chunk_recv_callback = spdy_cb_on_data_chunk_recv; - callbacks->on_data_recv_callback = spdy_cb_on_data_recv; -} - - -/* - * Callback function for SSL/TLS NPN. Since this program only supports - * SPDY protocol, if server does not offer SPDY protocol the Spdylay - * library supports, we terminate program. - */ -static int -spdy_cb_ssl_select_next_proto(SSL* ssl, - unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen, - void *arg) -{ - (void)ssl; - - int rv; - uint16_t *spdy_proto_version; - - /* spdylay_select_next_protocol() selects SPDY protocol version the - Spdylay library supports. */ - rv = spdylay_select_next_protocol(out, outlen, in, inlen); - if(rv <= 0) { - PRINT_INFO("Server did not advertise spdy/2 or spdy/3 protocol."); - return rv; - } - spdy_proto_version = (uint16_t*)arg; - *spdy_proto_version = rv; - return SSL_TLSEXT_ERR_OK; -} - - -/* - * Setup SSL context. We pass |spdy_proto_version| to get negotiated - * SPDY protocol version in NPN callback. - */ -void -spdy_ssl_init_ssl_ctx(SSL_CTX *ssl_ctx, - uint16_t *spdy_proto_version) -{ - /* Disable SSLv2 and enable all workarounds for buggy servers */ - SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); - /* Set NPN callback */ - SSL_CTX_set_next_proto_select_cb(ssl_ctx, spdy_cb_ssl_select_next_proto, - spdy_proto_version); -} - - -static int -spdy_ssl_handshake(SSL *ssl, - int fd) -{ - int rv; - - if(SSL_set_fd(ssl, fd) == 0) - spdy_dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL)); - - ERR_clear_error(); - rv = SSL_connect(ssl); - if(rv <= 0) - PRINT_INFO2("SSL_connect %s", ERR_error_string(ERR_get_error(), NULL)); - - return rv; -} - - -/* - * Connects to the host |host| and port |port|. This function returns - * the file descriptor of the client socket. - */ -static int -spdy_socket_connect_to(const char *host, - uint16_t port) -{ - struct addrinfo hints; - int fd = -1; - int rv; - char service[NI_MAXSERV]; - struct addrinfo *res, *rp; - - //TODO checks - snprintf(service, sizeof(service), "%u", port); - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - rv = getaddrinfo(host, service, &hints, &res); - if(rv != 0) - { - printf("%s\n",host); - spdy_dief("getaddrinfo", gai_strerror(rv)); - } - for(rp = res; rp; rp = rp->ai_next) - { - fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if(fd == -1) - continue; - while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && - errno == EINTR); - if(rv == 0) - break; - MHD_socket_close_ (fd); - fd = -1; - } - freeaddrinfo(res); - - return fd; -} - - -static void -spdy_socket_make_non_block(int fd) -{ - int flags; - int rv; - - while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR); - - if(flags == -1) - spdy_dief("fcntl", strerror(errno)); - - while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR); - - if(rv == -1) - spdy_dief("fcntl", strerror(errno)); -} - - -/* - * Setting TCP_NODELAY is not mandatory for the SPDY protocol. - */ -static void -spdy_socket_set_tcp_nodelay(int fd) -{ - int val = 1; - int rv; - - rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); - if(rv == -1) - spdy_dief("setsockopt", strerror(errno)); -} - -/* - * Update |pollfd| based on the state of |connection|. - */ - /* -void -spdy_ctl_poll(struct pollfd *pollfd, - struct SPDY_Connection *connection) -{ - pollfd->events = 0; - if(spdylay_session_want_read(connection->session) || - connection->want_io & WANT_READ) - { - pollfd->events |= POLLIN; - } - if(spdylay_session_want_write(connection->session) || - connection->want_io & WANT_WRITE) - { - pollfd->events |= POLLOUT; - } -}*/ - - -/* - * Update |selectfd| based on the state of |connection|. - */ -bool -spdy_ctl_select(fd_set * read_fd_set, - fd_set * write_fd_set, - fd_set * except_fd_set, - struct SPDY_Connection *connection) -{ - (void)except_fd_set; - - bool ret = false; - - if(spdylay_session_want_read(connection->session) || - connection->want_io & WANT_READ) - { - FD_SET(connection->fd, read_fd_set); - ret = true; - } - if(spdylay_session_want_write(connection->session) || - connection->want_io & WANT_WRITE) - { - FD_SET(connection->fd, write_fd_set); - ret = true; - } - - return ret; -} - - -/* - * Performs the network I/O. - */ -int -spdy_exec_io(struct SPDY_Connection *connection) -{ - int rv; - - rv = spdylay_session_recv(connection->session); - if(rv != 0) - { - PRINT_INFO2("spdylay_session_recv %i", rv); - return rv; - } - rv = spdylay_session_send(connection->session); - if(rv != 0) - PRINT_INFO2("spdylay_session_send %i", rv); - - return rv; -} - - -/* - * Fetches the resource denoted by |uri|. - */ -struct SPDY_Connection * -spdy_connect(const struct URI *uri, - uint16_t port, - bool is_tls) -{ - spdylay_session_callbacks callbacks; - int fd; - SSL *ssl=NULL; - struct SPDY_Connection * connection = NULL; - int rv; - - spdy_setup_spdylay_callbacks(&callbacks); - - /* Establish connection and setup SSL */ - PRINT_INFO2("connecting to %s:%i", uri->host, port); - fd = spdy_socket_connect_to(uri->host, port); - if(fd == -1) - { - PRINT_INFO("Could not open file descriptor"); - return NULL; - } - - if(is_tls) - { - ssl = SSL_new(glob_opt.ssl_ctx); - if(ssl == NULL) { - spdy_dief("SSL_new", ERR_error_string(ERR_get_error(), NULL)); - } - - //TODO non-blocking - /* To simplify the program, we perform SSL/TLS handshake in blocking - I/O. */ - glob_opt.spdy_proto_version = 0; - rv = spdy_ssl_handshake(ssl, fd); - if(rv <= 0 || (glob_opt.spdy_proto_version != 3 && glob_opt.spdy_proto_version != 2)) - { - PRINT_INFO("Closing SSL"); - //no spdy on the other side - goto free_and_fail; - } - } - else - { - glob_opt.spdy_proto_version = 3; - } - - if(NULL == (connection = au_malloc(sizeof(struct SPDY_Connection)))) - goto free_and_fail; - - connection->is_tls = is_tls; - connection->ssl = ssl; - connection->want_io = IO_NONE; - if(NULL == (connection->host = strdup(uri->host))) - goto free_and_fail; - - /* Here make file descriptor non-block */ - spdy_socket_make_non_block(fd); - spdy_socket_set_tcp_nodelay(fd); - - PRINT_INFO2("[INFO] SPDY protocol version = %d\n", glob_opt.spdy_proto_version); - rv = spdylay_session_client_new(&(connection->session), glob_opt.spdy_proto_version, - &callbacks, connection); - if(rv != 0) { - spdy_diec("spdylay_session_client_new", rv); - } - - connection->fd = fd; - - return connection; - - //for GOTO - free_and_fail: - if(NULL != connection) - { - free(connection->host); - free(connection); - } - - if(is_tls) - SSL_shutdown(ssl); - - MHD_socket_close_ (fd); - - if(is_tls) - SSL_free(ssl); - - return NULL; -} - - -void -spdy_free_connection(struct SPDY_Connection * connection) -{ - struct Proxy *proxy; - struct Proxy *proxy_next; - - if(NULL != connection) - { - for(proxy = connection->proxies_head; NULL != proxy; proxy=proxy_next) - { - proxy_next = proxy->next; - DLL_remove(connection->proxies_head, connection->proxies_tail, proxy); - proxy->spdy_active = false; - proxy->spdy_error = true; - PRINT_INFO2("spdy_free_connection for id %i", proxy->id); - if(!proxy->http_active) - { - free_proxy(proxy); - } - } - spdylay_session_del(connection->session); - SSL_free(connection->ssl); - free(connection->host); - free(connection); - //connection->session = NULL; - } -} - - -int -spdy_request(const char **nv, - struct Proxy *proxy, - bool with_body) -{ - int ret; - uint16_t port; - struct SPDY_Connection *connection; - spdylay_data_provider post_data; - - if(glob_opt.only_proxy) - { - connection = glob_opt.spdy_connection; - } - else - { - connection = glob_opt.spdy_connections_head; - while(NULL != connection) - { - if(0 == strcasecmp(proxy->uri->host, connection->host)) - break; - connection = connection->next; - } - - if(NULL == connection) - { - //connect to host - port = proxy->uri->port; - if(0 == port) port = 443; - connection = spdy_connect(proxy->uri, port, true); - if(NULL != connection) - { - DLL_insert(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection); - glob_opt.total_spdy_connections++; - } - else - connection = glob_opt.spdy_connection; - } - } - - if(NULL == connection) - { - PRINT_INFO("there is no proxy!"); - return -1; - } - - proxy->spdy_connection = connection; - if(with_body) - { - post_data.source.ptr = proxy; - post_data.read_callback = &spdy_cb_data_source_read; - ret = spdylay_submit_request(connection->session, 0, nv, &post_data, proxy); - } - else - ret = spdylay_submit_request(connection->session, 0, nv, NULL, proxy); - - if(ret != 0) { - spdy_diec("spdylay_spdy_submit_request", ret); - } - PRINT_INFO2("adding proxy %i", proxy->id); - if(NULL != connection->proxies_head) - PRINT_INFO2("before proxy %i", connection->proxies_head->id); - DLL_insert(connection->proxies_head, connection->proxies_tail, proxy); - - return ret; -} - -/* -void -spdy_get_pollfdset(struct pollfd fds[], - struct SPDY_Connection *connections[], - unsigned int max_size, - nfds_t *real_size) -{ - struct SPDY_Connection *connection; - struct Proxy *proxy; - - *real_size = 0; - if(max_size<1) - return; - - if(NULL != glob_opt.spdy_connection) - { - spdy_ctl_poll(&(fds[*real_size]), glob_opt.spdy_connection); - if(!fds[*real_size].events) - { - //PRINT_INFO("TODO drop connection"); - glob_opt.streams_opened -= glob_opt.spdy_connection->streams_opened; - - for(proxy = glob_opt.spdy_connection->proxies_head; NULL != proxy; proxy=proxy->next) - { - abort(); - DLL_remove(glob_opt.spdy_connection->proxies_head, glob_opt.spdy_connection->proxies_tail, proxy); - proxy->spdy_active = false; - } - spdy_free_connection(glob_opt.spdy_connection); - glob_opt.spdy_connection = NULL; - } - else - { - fds[*real_size].fd = glob_opt.spdy_connection->fd; - connections[*real_size] = glob_opt.spdy_connection; - ++(*real_size); - } - } - - connection = glob_opt.spdy_connections_head; - - while(NULL != connection && *real_size < max_size) - { - assert(!glob_opt.only_proxy); - spdy_ctl_poll(&(fds[*real_size]), connection); - if(!fds[*real_size].events) - { - //PRINT_INFO("TODO drop connection"); - glob_opt.streams_opened -= connection->streams_opened; - DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection); - glob_opt.total_spdy_connections--; - - for(proxy = connection->proxies_head; NULL != proxy; proxy=proxy->next) - { - abort(); - DLL_remove(connection->proxies_head, connection->proxies_tail, proxy); - proxy->spdy_active = false; - } - spdy_free_connection(connection); - } - else - { - fds[*real_size].fd = connection->fd; - connections[*real_size] = connection; - ++(*real_size); - } - connection = connection->next; - } - - //, "TODO max num of conn reached; close something" - assert(NULL == connection); -} -*/ - -int -spdy_get_selectfdset(fd_set * read_fd_set, - fd_set * write_fd_set, - fd_set * except_fd_set, - struct SPDY_Connection *connections[], - unsigned int max_size, - nfds_t *real_size) -{ - struct SPDY_Connection *connection; - struct SPDY_Connection *next_connection; - bool ret; - int maxfd = 0; - - *real_size = 0; - if(max_size<1) - return 0; - - if(NULL != glob_opt.spdy_connection) - { - ret = spdy_ctl_select(read_fd_set, - write_fd_set, - except_fd_set, glob_opt.spdy_connection); - if(!ret) - { - glob_opt.streams_opened -= glob_opt.spdy_connection->streams_opened; - - PRINT_INFO("spdy_free_connection in spdy_get_selectfdset"); - spdy_free_connection(glob_opt.spdy_connection); - glob_opt.spdy_connection = NULL; - } - else - { - connections[*real_size] = glob_opt.spdy_connection; - ++(*real_size); - if(maxfd < glob_opt.spdy_connection->fd) maxfd = glob_opt.spdy_connection->fd; - } - } - - connection = glob_opt.spdy_connections_head; - - while(NULL != connection && *real_size < max_size) - { - assert(!glob_opt.only_proxy); - ret = spdy_ctl_select(read_fd_set, - write_fd_set, - except_fd_set, connection); - - next_connection = connection->next; - if(!ret) - { - glob_opt.streams_opened -= connection->streams_opened; - DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection); - glob_opt.total_spdy_connections--; - - PRINT_INFO("spdy_free_connection in spdy_get_selectfdset"); - spdy_free_connection(connection); - } - else - { - connections[*real_size] = connection; - ++(*real_size); - if(maxfd < connection->fd) maxfd = connection->fd; - } - connection = next_connection; - } - - //, "TODO max num of conn reached; close something" - assert(NULL == connection); - - return maxfd; -} - -/* -void -spdy_run(struct pollfd fds[], - struct SPDY_Connection *connections[], - int size) -{ - int i; - int ret; - struct Proxy *proxy; - - for(i=0; i<size; ++i) - { - // PRINT_INFO2("exec about to be called for %s", connections[i]->host); - if(fds[i].revents & (POLLIN | POLLOUT)) - { - ret = spdy_exec_io(connections[i]); - //PRINT_INFO2("%i",ret); - //if((spdy_pollfds[i].revents & POLLHUP) || (spdy_pollfds[0].revents & POLLERR)) - // PRINT_INFO("SPDY SPDY_Connection error"); - - //TODO POLLRDHUP - // always close on ret != 0? - - if(0 != ret) - { - glob_opt.streams_opened -= connections[i]->streams_opened; - if(connections[i] == glob_opt.spdy_connection) - { - glob_opt.spdy_connection = NULL; - } - else - { - DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connections[i]); - glob_opt.total_spdy_connections--; - } - for(proxy = connections[i]->proxies_head; NULL != proxy; proxy=proxy->next) - { - abort(); - DLL_remove(connections[i]->proxies_head, connections[i]->proxies_tail, proxy); - proxy->spdy_active = false; - proxy->spdy_error = true; - PRINT_INFO2("spdy_free_connection for id %i", proxy->id); - } - PRINT_INFO("spdy_free_connection in loop"); - spdy_free_connection(connections[i]); - } - } - else - PRINT_INFO("not called"); - } -} -*/ - -void -spdy_run_select(fd_set * read_fd_set, - fd_set * write_fd_set, - fd_set * except_fd_set, - struct SPDY_Connection *connections[], - int size) -{ - int i; - int ret; - - for(i=0; i<size; ++i) - { - // PRINT_INFO2("exec about to be called for %s", connections[i]->host); - if(FD_ISSET(connections[i]->fd, read_fd_set) || FD_ISSET(connections[i]->fd, write_fd_set) || FD_ISSET(connections[i]->fd, except_fd_set)) - { - //raise(SIGINT); - ret = spdy_exec_io(connections[i]); - - if(0 != ret) - { - glob_opt.streams_opened -= connections[i]->streams_opened; - if(connections[i] == glob_opt.spdy_connection) - { - glob_opt.spdy_connection = NULL; - } - else - { - DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connections[i]); - glob_opt.total_spdy_connections--; - } - PRINT_INFO("in spdy_run_select"); - spdy_free_connection(connections[i]); - } - } - else - { - PRINT_INFO("not called"); - //PRINT_INFO2("connection->want_io %i",connections[i]->want_io); - //PRINT_INFO2("read %i",spdylay_session_want_read(connections[i]->session)); - //PRINT_INFO2("write %i",spdylay_session_want_write(connections[i]->session)); - //raise(SIGINT); - } - } -} diff --git a/src/examples/mhd2spdy_spdy.h b/src/examples/mhd2spdy_spdy.h @@ -1,102 +0,0 @@ -/* - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file mhd2spdy_spdy.h - * @brief SPDY part of the proxy. libspdylay is used for the client side. - * @author Andrey Uzunov - */ - -#ifndef SPDY_H -#define SPDY_H - -#include "mhd2spdy_structures.h" - - -struct SPDY_Connection * -spdy_connect(const struct URI *uri, - uint16_t port, - bool is_tls); - - -void -spdy_ctl_poll(struct pollfd *pollfd, - struct SPDY_Connection *connection); - - -bool -spdy_ctl_select(fd_set * read_fd_set, - fd_set * write_fd_set, - fd_set * except_fd_set, - struct SPDY_Connection *connection); - - -int -spdy_exec_io(struct SPDY_Connection *connection); - - -void -spdy_diec(const char *func, - int error_code); - - -int -spdy_request(const char **nv, - struct Proxy *proxy, - bool with_body); - - -void -spdy_ssl_init_ssl_ctx(SSL_CTX *ssl_ctx, - uint16_t *spdy_proto_version); - - -void -spdy_free_connection(struct SPDY_Connection * connection); - - -void -spdy_get_pollfdset(struct pollfd fds[], - struct SPDY_Connection *connections[], - unsigned int max_size, - nfds_t *real_size); - - -int -spdy_get_selectfdset(fd_set * read_fd_set, - fd_set * write_fd_set, - fd_set * except_fd_set, - struct SPDY_Connection *connections[], - unsigned int max_size, - nfds_t *real_size); - - -void -spdy_run(struct pollfd fds[], - struct SPDY_Connection *connections[], - int size); - - -void -spdy_run_select(fd_set * read_fd_set, - fd_set * write_fd_set, - fd_set * except_fd_set, - struct SPDY_Connection *connections[], - int size); - - -#endif diff --git a/src/examples/mhd2spdy_structures.c b/src/examples/mhd2spdy_structures.c @@ -1,162 +0,0 @@ -/* - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file mhd2spdy_structures.h - * @brief Common functions, macros. - * @author Andrey Uzunov - */ - -#include "mhd2spdy_structures.h" - - -void -free_uri(struct URI * uri) -{ - if(NULL != uri) - { - free(uri->full_uri); - free(uri->scheme); - free(uri->host_and_port); - free(uri->host); - free(uri->path); - free(uri->path_and_more); - free(uri->query); - free(uri->fragment); - uri->port = 0; - free(uri); - } -} - - -int -init_parse_uri(regex_t * preg) -{ - // RFC 2396 - // ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? - /* - scheme = $2 - authority = $4 - path = $5 - query = $7 - fragment = $9 - */ - - return regcomp(preg, "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", REG_EXTENDED); -} - -void -deinit_parse_uri(regex_t * preg) -{ - regfree(preg); -} - -int -parse_uri(regex_t * preg, - char * full_uri, - struct URI ** uri) -{ - int ret; - char *colon; - long long port; - size_t nmatch = 10; - regmatch_t pmatch[10]; - - if (0 != (ret = regexec(preg, full_uri, nmatch, pmatch, 0))) - return ret; - - *uri = au_malloc(sizeof(struct URI)); - if(NULL == *uri) - return -200; - - (*uri)->full_uri = strdup(full_uri); - - asprintf(&((*uri)->scheme), "%.*s",pmatch[2].rm_eo - pmatch[2].rm_so, &full_uri[pmatch[2].rm_so]); - asprintf(&((*uri)->host_and_port), "%.*s",pmatch[4].rm_eo - pmatch[4].rm_so, &full_uri[pmatch[4].rm_so]); - asprintf(&((*uri)->path), "%.*s",pmatch[5].rm_eo - pmatch[5].rm_so, &full_uri[pmatch[5].rm_so]); - asprintf(&((*uri)->path_and_more), "%.*s",pmatch[9].rm_eo - pmatch[5].rm_so, &full_uri[pmatch[5].rm_so]); - asprintf(&((*uri)->query), "%.*s",pmatch[7].rm_eo - pmatch[7].rm_so, &full_uri[pmatch[7].rm_so]); - asprintf(&((*uri)->fragment), "%.*s",pmatch[9].rm_eo - pmatch[9].rm_so, &full_uri[pmatch[9].rm_so]); - - colon = strrchr((*uri)->host_and_port, ':'); - if(NULL == colon) - { - (*uri)->host = strdup((*uri)->host_and_port); - (*uri)->port = 0; - - return 0; - } - - port = atoi(colon + 1); - if(port<1 || port >= 256 * 256) - { - free_uri(*uri); - return -100; - } - (*uri)->port = port; - asprintf(&((*uri)->host), "%.*s", (int)(colon - (*uri)->host_and_port), (*uri)->host_and_port); - - return 0; -} - - -void -free_proxy(struct Proxy *proxy) -{ - PRINT_INFO2("free proxy called for '%s'", proxy->url); - if(NULL != proxy->http_body && proxy->http_body_size > 0) - UPDATE_STAT(glob_stat.spdy_bytes_received_and_dropped, proxy->http_body_size); - free(proxy->http_body); - free_uri(proxy->uri); - free(proxy->url); - free(proxy->http_uri); - free(proxy); -} - - -void *au_malloc(size_t size) -{ - void *new_memory; - - new_memory = malloc(size); - if(NULL != new_memory) - { - glob_opt.global_memory += size; - memset(new_memory, 0, size); - } - return new_memory; -} - - -bool -copy_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size) -{ - if(0 == src_size) - return true; - - if(NULL == *dst) - *dst = malloc(src_size); - else - *dst = realloc(*dst, src_size + *dst_size); - if(NULL == *dst) - return false; - - memcpy(*dst + *dst_size, src, src_size); - *dst_size += src_size; - - return true; -} diff --git a/src/examples/mhd2spdy_structures.h b/src/examples/mhd2spdy_structures.h @@ -1,296 +0,0 @@ -/* - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file mhd2spdy_structures.h - * @brief Common structures, functions, macros and global variables. - * @author Andrey Uzunov - */ -#ifndef STRUCTURES_H -#define STRUCTURES_H - -#define _GNU_SOURCE - -#include <unistd.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <string.h> -#include <stdio.h> -#include <ctype.h> -#include <errno.h> -#include <assert.h> -#include <microhttpd.h> -#include <signal.h> -#include <poll.h> -#include <fcntl.h> -#include <regex.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <openssl/ssl.h> -#include <openssl/err.h> -#include <spdylay/spdylay.h> -#include <getopt.h> - - -/* WANT_READ if SSL connection needs more input; or WANT_WRITE if it - needs more output; or IO_NONE. This is necessary because SSL/TLS - re-negotiation is possible at any time. Spdylay API offers - similar functions like spdylay_session_want_read() and - spdylay_session_want_write() but they do not take into account - SSL connection. */ -enum -{ - IO_NONE, - WANT_READ, - WANT_WRITE -}; - - -struct Proxy; - - -struct SPDY_Connection { - SSL *ssl; - spdylay_session *session; - struct SPDY_Connection *prev; - struct SPDY_Connection *next; - struct Proxy *proxies_head; - struct Proxy *proxies_tail; - char *host; - int fd; - int want_io; - uint counter; - uint streams_opened; - bool is_tls; -}; - - -struct URI -{ - char * full_uri; - char * scheme; - char * host_and_port; - char * host; - char * path; - char * path_and_more; - char * query; - char * fragment; - uint16_t port; -}; - - -struct HTTP_URI; - - -struct Proxy -{ - struct MHD_Connection *http_connection; - struct MHD_Response *http_response; - struct URI *uri; - struct HTTP_URI *http_uri; - struct SPDY_Connection *spdy_connection; - struct Proxy *next; - struct Proxy *prev; - char *url; - char *version; - void *http_body; - void *received_body; - size_t http_body_size; - size_t received_body_size; - ssize_t length; - int status; - int id; - int32_t stream_id; - bool done; - bool http_error; - bool spdy_error; - bool http_active; - bool spdy_active; - bool receiving_done; -}; - - -struct HTTP_URI -{ - char * uri; - struct Proxy * proxy; -}; - - -struct SPDY_Headers -{ - const char **nv; - int num; - int cnt; -}; - - -struct global_options -{ - char *spdy2http_str; - struct SPDY_Connection *spdy_connection; - struct SPDY_Connection *spdy_connections_head; - struct SPDY_Connection *spdy_connections_tail; - int streams_opened; - int responses_pending; - regex_t uri_preg; - size_t global_memory; - SSL_CTX *ssl_ctx; - uint32_t total_spdy_connections; - uint16_t spdy_proto_version; - uint16_t listen_port; - bool verbose; - bool only_proxy; - bool spdy_data_received; - bool statistics; - bool ignore_rst_stream; -} -glob_opt; - - -struct global_statistics -{ - //unsigned long long http_bytes_sent; - //unsigned long long http_bytes_received; - unsigned long long spdy_bytes_sent; - unsigned long long spdy_bytes_received; - unsigned long long spdy_bytes_received_and_dropped; -} -glob_stat; - - -//forbidden headers -#define SPDY_HTTP_HEADER_TRANSFER_ENCODING "transfer-encoding" -#define SPDY_HTTP_HEADER_PROXY_CONNECTION "proxy-connection" -#define SPDY_HTTP_HEADER_KEEP_ALIVE "keep-alive" -#define SPDY_HTTP_HEADER_CONNECTION "connection" - -#define MAX_SPDY_CONNECTIONS 100 - -#define SPDY_MAX_OUTLEN 4096 - -/** - * Insert an element at the head of a DLL. Assumes that head, tail and - * element are structs with prev and next fields. - * - * @param head pointer to the head of the DLL (struct ? *) - * @param tail pointer to the tail of the DLL (struct ? *) - * @param element element to insert (struct ? *) - */ -#define DLL_insert(head,tail,element) do { \ - (element)->next = (head); \ - (element)->prev = NULL; \ - if ((tail) == NULL) \ - (tail) = element; \ - else \ - (head)->prev = element; \ - (head) = (element); } while (0) - - -/** - * Remove an element from a DLL. Assumes - * that head, tail and element are structs - * with prev and next fields. - * - * @param head pointer to the head of the DLL (struct ? *) - * @param tail pointer to the tail of the DLL (struct ? *) - * @param element element to remove (struct ? *) - */ -#define DLL_remove(head,tail,element) do { \ - if ((element)->prev == NULL) \ - (head) = (element)->next; \ - else \ - (element)->prev->next = (element)->next; \ - if ((element)->next == NULL) \ - (tail) = (element)->prev; \ - else \ - (element)->next->prev = (element)->prev; \ - (element)->next = NULL; \ - (element)->prev = NULL; } while (0) - - -#define PRINT_INFO(msg) do{\ - if(glob_opt.verbose){\ - printf("%i:%s\n", __LINE__, msg);\ - fflush(stdout);\ - }\ - }\ - while(0) - - -#define PRINT_INFO2(fmt, ...) do{\ - if(glob_opt.verbose){\ - printf("%i\n", __LINE__);\ - printf(fmt,##__VA_ARGS__);\ - printf("\n");\ - fflush(stdout);\ - }\ - }\ - while(0) - - -#define DIE(msg) do{\ - printf("FATAL ERROR (line %i): %s\n", __LINE__, msg);\ - fflush(stdout);\ - exit(EXIT_FAILURE);\ - }\ - while(0) - - -#define UPDATE_STAT(stat, value) do{\ - if(glob_opt.statistics)\ - {\ - stat += value;\ - }\ - }\ - while(0) - - -void -free_uri(struct URI * uri); - - -int -init_parse_uri(regex_t * preg); - - -void -deinit_parse_uri(regex_t * preg); - - -int -parse_uri(regex_t * preg, - char * full_uri, - struct URI ** uri); - - -void -free_proxy(struct Proxy *proxy); - - -void * -au_malloc(size_t size); - - -bool -copy_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size); - -#endif diff --git a/src/examples/spdy_event_loop.c b/src/examples/spdy_event_loop.c @@ -1,487 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file event_loop.c - * @brief shows how to use the daemon. THIS IS MAINLY A TEST AND DEBUG - * PROGRAM - * @author Andrey Uzunov - */ - -#include "platform.h" -#include <unistd.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <string.h> -#include <stdio.h> -#include <ctype.h> -#include <errno.h> -#include "microspdy.h" -#include <sys/time.h> -#include <time.h> -#ifndef MINGW -#include <arpa/inet.h> -#endif -//#include "../framinglayer/structures.h" -//#include "../applicationlayer/alstructures.h" - -static int run = 1; - -static int run2 = 1; - - -static uint64_t loops; - -static time_t start; - - -static void -new_session_callback (void *cls, - struct SPDY_Session * session) -{ - (void)cls; - - char ipstr[1024]; - - struct sockaddr *addr; - socklen_t addr_len = SPDY_get_remote_addr(session, &addr); - - if(!addr_len) - { - printf("SPDY_get_remote_addr"); - abort(); - } - - if(AF_INET == addr->sa_family) - { - struct sockaddr_in * addr4 = (struct sockaddr_in *) addr; - if(NULL == inet_ntop(AF_INET, &(addr4->sin_addr), ipstr, sizeof(ipstr))) - { - printf("inet_ntop"); - abort(); - } - printf("New connection from: %s:%i\n", ipstr, ntohs(addr4->sin_port)); - - } - else if(AF_INET6 == addr->sa_family) - { - struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *) addr; - if(NULL == inet_ntop(AF_INET6, &(addr6->sin6_addr), ipstr, sizeof(ipstr))) - { - printf("inet_ntop"); - abort(); - } - printf("New connection from: %s:%i\n", ipstr, ntohs(addr6->sin6_port)); - - } -} - - -static void -session_closed_handler (void *cls, - struct SPDY_Session * session, - int by_client) -{ - (void)cls; - (void)session; - - //printf("session_closed_handler called\n"); - - if(SPDY_YES != by_client) - { - //killchild(child,"wrong by_client"); - printf("session closed by server\n"); - } - else - { - printf("session closed by client\n"); - } - - //session_closed_called = 1; -} - - -static void -response_done_callback(void *cls, - struct SPDY_Response *response, - struct SPDY_Request *request, - enum SPDY_RESPONSE_RESULT status, - bool streamopened) -{ - (void)streamopened; - if(strcmp(cls, "/close (daemon1)") == 0) - run = 0; - else { - if(strcmp(cls, "/close (daemon2)") == 0) run2 = 0; - loops = 0; - start = time(NULL); - } - if(SPDY_RESPONSE_RESULT_SUCCESS != status) - { - printf("not sent frame cause %i", status); - } - printf("answer for %s was sent\n", (char*)cls); - //printf("raw sent headers %s\n", (char *)(response->headers)+8); - - SPDY_destroy_request(request); - SPDY_destroy_response(response); - free(cls); -} - -/* -static int -print_headers (void *cls, - const char *name, const char *value) -{ - (void)cls; - printf("%s: %s\n",name,value); - return SPDY_YES; -} - */ - - -/* -void -new_request_cb (void *cls, - struct SPDY_Request * request, - uint8_t priority, - const char *method, - const char *path, - const char *version, - const char *host, - const char *scheme, - struct SPDY_NameValue * headers) -{ - (void)cls; - (void)request; - printf("Priority: %i\nHTTP headers, scheme: %s\n\n%s %s %s\nHost: %s\n", priority,scheme,method,path,version,host); - SPDY_name_value_iterate(headers, &print_headers, NULL); -} -*/ - - -static int -append_headers_to_data (void *cls, - const char *name, const char * const *value, int num_values) -{ - char **data = cls; - void *tofree = *data; - int i; - - if(num_values) - for(i=0;i<num_values;++i) - { - asprintf(data,"%s%s: %s\n", *data,name,value[i]); - } - else - asprintf(data,"%s%s: \n", *data,name); - - free(tofree); - return SPDY_YES; -} - - -static void -standard_request_handler(void *cls, - struct SPDY_Request * request, - uint8_t priority, - const char *method, - const char *path, - const char *version, - const char *host, - const char *scheme, - struct SPDY_NameValue * headers, - bool more) -{ - (void)more; - - char *html; - char *data; - struct SPDY_Response *response=NULL; - - printf("received request for '%s %s %s'\n", method, path, version); - if(strcmp(path,"/main.css")==0) - { - if(NULL != cls) - asprintf(&html,"body{color:green;}"); - else - asprintf(&html,"body{color:red;}"); - - //struct SPDY_NameValue *headers=SPDY_name_value_create(); - //SPDY_name_value_add(headers,"content-type","text/css"); - - response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,html,strlen(html)); - free(html); - } - else - { - asprintf(&data,"Priority: %i\nHTTP headers, scheme: %s\n\n%s %s %s\nHost: %s\n", priority,scheme,method,path,version,host); - - SPDY_name_value_iterate(headers, &append_headers_to_data, &data); - - if(strcmp(path,"/close")==0) - { - asprintf(&html,"<html>" - "<body><b>Closing now!<br>This is an answer to the following " - "request:</b><br><br><pre>%s</pre></body></html>",data); - } - else - { - asprintf(&html,"<html><link href=\"main.css\" rel=\"stylesheet\" type=\"text/css\" />" - "<body><b>This is an answer to the following " - "request:</b><br><br><pre>%s</pre></body></html>",data); - } - - free(data); - - response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,html,strlen(html)); - free(html); - } - - if(NULL==response){ - fprintf(stdout,"no response obj\n"); - abort(); - } - - char *pathcls; - asprintf(&pathcls, "%s (daemon%i)",path,NULL==cls ? 1 : 2); - if(SPDY_queue_response(request,response,true,false,&response_done_callback,pathcls)!=SPDY_YES) - { - fprintf(stdout,"queue\n"); - abort(); - } -} - - -static int -new_post_data_cb (void * cls, - struct SPDY_Request *request, - const void * buf, - size_t size, - bool more) -{ - (void)cls; - (void)request; - (void)more; - - printf("DATA:\n===============================\n"); - write(0, buf, size); - printf("\n===============================\n"); - return SPDY_YES; -} - - -static void -sig_handler(int signo) -{ - (void)signo; - - printf("received signal\n"); -} - - -int -main (int argc, char *const *argv) -{ - if(argc != 2) return 1; - - #ifndef MINGW - if (signal(SIGPIPE, sig_handler) == SIG_ERR) - printf("\ncan't catch SIGPIPE\n"); - #endif - - SPDY_init(); - - /* - struct sockaddr_in addr4; - struct in_addr inaddr4; - inaddr4.s_addr = htonl(INADDR_ANY); - addr4.sin_family = AF_INET; - addr4.sin_addr = inaddr4; - addr4.sin_port = htons(atoi(argv[1])); - */ - - struct SPDY_Daemon *daemon = SPDY_start_daemon(atoi(argv[1]), - DATA_DIR "cert-and-key.pem", - DATA_DIR "cert-and-key.pem", - &new_session_callback,&session_closed_handler,&standard_request_handler,&new_post_data_cb,NULL, - SPDY_DAEMON_OPTION_SESSION_TIMEOUT, 10, - //SPDY_DAEMON_OPTION_SOCK_ADDR, (struct sockaddr *)&addr4, - SPDY_DAEMON_OPTION_END); - - if(NULL==daemon){ - printf("no daemon\n"); - return 1; - } - - /* - struct sockaddr_in6 addr6; - addr6.sin6_family = AF_INET6; - addr6.sin6_addr = in6addr_any; - addr6.sin6_port = htons(atoi(argv[1]) + 1); - */ - - struct SPDY_Daemon *daemon2 = SPDY_start_daemon(atoi(argv[1]) + 1, - DATA_DIR "cert-and-key.pem", - DATA_DIR "cert-and-key.pem", - &new_session_callback,NULL,&standard_request_handler,&new_post_data_cb,&main, - //SPDY_DAEMON_OPTION_SESSION_TIMEOUT, 0, - //SPDY_DAEMON_OPTION_SOCK_ADDR, (struct sockaddr *)&addr6, - //SPDY_DAEMON_OPTION_FLAGS, SPDY_DAEMON_FLAG_ONLY_IPV6, - SPDY_DAEMON_OPTION_END); - - if(NULL==daemon2){ - printf("no daemon\n"); - return 1; - } - - do - { - unsigned long long timeoutlong=0; - struct timeval timeout; - volatile int rc; /* select() return code */ - volatile int ret; - - fd_set read_fd_set; - fd_set write_fd_set; - fd_set except_fd_set; - int maxfd = -1; - - if(run && daemon != NULL) - { - loops++; - FD_ZERO(&read_fd_set); - FD_ZERO(&write_fd_set); - FD_ZERO(&except_fd_set); - - ret = SPDY_get_timeout(daemon, &timeoutlong); - if(SPDY_NO == ret || timeoutlong > 1000) - { - timeout.tv_sec = 1; - timeout.tv_usec = 0; - } - else - { - timeout.tv_sec = timeoutlong / 1000; - timeout.tv_usec = (timeoutlong % 1000) * 1000; - } - - printf("ret=%i; timeoutlong=%llu; sec=%llu; usec=%llu\n", ret, timeoutlong, (long long unsigned)timeout.tv_sec, (long long unsigned)timeout.tv_usec); - //raise(SIGINT); - - /* get file descriptors from the transfers */ - maxfd = SPDY_get_fdset (daemon, - &read_fd_set, - &write_fd_set, - &except_fd_set); - -//struct timeval ts1,ts2; - //gettimeofday(&ts1, NULL); - rc = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); - //gettimeofday(&ts2, NULL); - printf("rc %i\n",rc); - // printf("time for select %i\n",ts2.tv_usec - ts1.tv_usec); - // printf("%i %i %i %i\n",ts1.tv_sec, ts1.tv_usec,ts2.tv_sec, ts2.tv_usec); - - switch(rc) { - case -1: - /* select error */ - break; - case 0: - - break; - default: - SPDY_run(daemon); - - break; - } - } - else if(daemon != NULL){ - - printf("%lu loops in %llu secs\n", loops, (long long unsigned)(time(NULL) - start)); - SPDY_stop_daemon(daemon); - daemon=NULL; - } - - if(run2) - { - FD_ZERO(&read_fd_set); - FD_ZERO(&write_fd_set); - FD_ZERO(&except_fd_set); - - ret = SPDY_get_timeout(daemon2, &timeoutlong); - //printf("tout %i\n",timeoutlong); - if(SPDY_NO == ret || timeoutlong > 1) - { - //do sth else - //sleep(1); - - //try new connection - timeout.tv_sec = 1; - timeout.tv_usec = 0; - } - else - { - timeout.tv_sec = timeoutlong; - timeout.tv_usec = 0;//(timeoutlong % 1000) * 1000; - } - - //printf("ret=%i; timeoutlong=%i; sec=%i; usec=%i\n", ret, timeoutlong, timeout.tv_sec, timeout.tv_usec); - //raise(SIGINT); - - /* get file descriptors from the transfers */ - maxfd = SPDY_get_fdset (daemon2, - &read_fd_set, - &write_fd_set, - &except_fd_set); - - rc = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); - - switch(rc) { - case -1: - /* select error */ - break; - case 0: - - break; - default: - SPDY_run(daemon2); - - break; - } - } - else if(daemon2 != NULL){ - SPDY_stop_daemon(daemon2); - daemon2=NULL; - } - } - while(run || run2); - - if(daemon != NULL){ - SPDY_stop_daemon(daemon); - } - if(daemon2 != NULL){ - SPDY_stop_daemon(daemon2); - } - - SPDY_deinit(); - - return 0; -} - diff --git a/src/examples/spdy_fileserver.c b/src/examples/spdy_fileserver.c @@ -1,353 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file fileserver.c - * @brief Simple example how the lib can be used for serving - * files directly read from the system - * @author Andrey Uzunov - */ - -//for asprintf -#define _GNU_SOURCE - -#include <unistd.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <string.h> -#include <stdio.h> -#include <ctype.h> -#include <errno.h> -#include "microspdy.h" -#include "time.h" - - -int run = 1; -char* basedir; - - -#define GET_MIME_TYPE(fname, mime) do {\ - unsigned int __len = strlen(fname);\ - if (__len < 4 || '.' != (fname)[__len - 4]) \ - { \ - (mime) = strdup("application/octet-stream");\ - printf("MIME for %s is applic...\n", (fname));\ - }\ - else {\ - const char * __ext = &(fname)[__len - 3];\ - if(0 == strcmp(__ext, "jpg")) (mime) = strdup("image/jpeg");\ - else if(0 == strcmp(__ext, "png")) (mime) = strdup("image/png");\ - else if(0 == strcmp(__ext, "css")) (mime) = strdup("text/css");\ - else if(0 == strcmp(__ext, "gif")) (mime) = strdup("image/gif");\ - else if(0 == strcmp(__ext, "htm")) (mime) = strdup("text/html");\ - else \ - { \ - (mime) = strdup("application/octet-stream");\ - printf("MIME for %s is applic...\n", (fname));\ - }\ - }\ - if(NULL == (mime))\ - {\ - printf("no memory\n");\ - abort();\ - }\ - } while (0) - - -static const char *DAY_NAMES[] = - { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - -static const char *MONTH_NAMES[] = - { "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - -//taken from http://stackoverflow.com/questions/2726975/how-can-i-generate-an-rfc1123-date-string-from-c-code-win32 -//and modified for linux -char *Rfc1123_DateTimeNow() -{ - const int RFC1123_TIME_LEN = 29; - time_t t; - struct tm tm; - char * buf = malloc(RFC1123_TIME_LEN+1); - - if (NULL == buf) - return NULL; - time(&t); - gmtime_r( &t, &tm); - - strftime(buf, RFC1123_TIME_LEN+1, "---, %d --- %Y %H:%M:%S GMT", &tm); - memcpy(buf, DAY_NAMES[tm.tm_wday], 3); - memcpy(buf+8, MONTH_NAMES[tm.tm_mon], 3); - - return buf; -} - - -ssize_t -response_callback (void *cls, - void *buffer, - size_t max, - bool *more) -{ - FILE *fd =(FILE*)cls; - - int ret = fread(buffer,1,max,fd); - *more = feof(fd) == 0; - - //if(!(*more)) - // fclose(fd); - - return ret; -} - - -void -response_done_callback(void *cls, - struct SPDY_Response *response, - struct SPDY_Request *request, - enum SPDY_RESPONSE_RESULT status, - bool streamopened) -{ - (void)streamopened; - (void)status; - //printf("answer for %s was sent\n", (char *)cls); - - /*if(SPDY_RESPONSE_RESULT_SUCCESS != status) - { - printf("answer for %s was NOT sent, %i\n", (char *)cls,status); - }*/ - - SPDY_destroy_request(request); - SPDY_destroy_response(response); - if(NULL!=cls)fclose(cls); -} - -void -standard_request_handler(void *cls, - struct SPDY_Request * request, - uint8_t priority, - const char *method, - const char *path, - const char *version, - const char *host, - const char *scheme, - struct SPDY_NameValue * headers, - bool more) -{ - (void)cls; - (void)request; - (void)priority; - (void)host; - (void)scheme; - (void)headers; - (void)method; - (void)version; - (void)more; - - struct SPDY_Response *response=NULL; - struct SPDY_NameValue *resp_headers; - char *fname; - char *fsize; - char *mime=NULL; - char *date=NULL; - ssize_t filesize = -666; - FILE *fd = NULL; - int ret = -666; - - //printf("received request for '%s %s %s'\n", method, path, version); - if(strlen(path) > 1 && NULL == strstr(path, "..") && '/' == path[0]) - { - asprintf(&fname,"%s%s",basedir,path); - if(0 == access(fname, R_OK)) - { - if(NULL == (fd = fopen(fname,"r")) - || 0 != (ret = fseek(fd, 0L, SEEK_END)) - || -1 == (filesize = ftell(fd)) - || 0 != (ret = fseek(fd, 0L, SEEK_SET))) - { - printf("Error on opening %s\n%p %i %zd\n",fname, fd, ret, filesize); - response = SPDY_build_response(SPDY_HTTP_INTERNAL_SERVER_ERROR,NULL,SPDY_HTTP_VERSION_1_1,NULL,NULL,0); - } - else - { - if(NULL == (resp_headers = SPDY_name_value_create())) - { - printf("SPDY_name_value_create failed\n"); - abort(); - } - - date = Rfc1123_DateTimeNow(); - if(NULL == date - || SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_DATE,date)) - { - printf("SPDY_name_value_add or Rfc1123_DateTimeNow failed\n"); - abort(); - } - free(date); - - if(-1 == asprintf(&fsize, "%zd", filesize) - || SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_LENGTH,fsize)) - { - printf("SPDY_name_value_add or asprintf failed\n"); - abort(); - } - free(fsize); - - GET_MIME_TYPE(path,mime); - if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_TYPE,mime)) - { - printf("SPDY_name_value_add failed\n"); - abort(); - } - free(mime); - - if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_SERVER,"libmicrospdy/fileserver")) - { - printf("SPDY_name_value_add failed\n"); - abort(); - } - - response = SPDY_build_response_with_callback(200,NULL, - SPDY_HTTP_VERSION_1_1,resp_headers,&response_callback,fd,SPDY_MAX_SUPPORTED_FRAME_SIZE); - SPDY_name_value_destroy(resp_headers); - } - - if(NULL==response){ - printf("no response obj\n"); - abort(); - } - - if(SPDY_queue_response(request,response,true,false,&response_done_callback,fd)!=SPDY_YES) - { - printf("queue\n"); - abort(); - } - - free(fname); - return; - } - free(fname); - } - - if(strcmp(path,"/close")==0) - { - run = 0; - } - - response = SPDY_build_response(SPDY_HTTP_NOT_FOUND,NULL,SPDY_HTTP_VERSION_1_1,NULL,NULL,0); - printf("Not found %s\n",path); - - if(NULL==response){ - printf("no response obj\n"); - abort(); - } - - if(SPDY_queue_response(request,response,true,false,&response_done_callback,NULL)!=SPDY_YES) - { - printf("queue\n"); - abort(); - } -} - -int -main (int argc, char *const *argv) -{ - unsigned long long timeoutlong=0; - struct timeval timeout; - int ret; - fd_set read_fd_set; - fd_set write_fd_set; - fd_set except_fd_set; - int maxfd = -1; - struct SPDY_Daemon *daemon; - - if(argc != 5) - { - printf("Usage: %s cert-file key-file base-dir port\n", argv[0]); - return 1; - } - - SPDY_init(); - - daemon = SPDY_start_daemon(atoi(argv[4]), - argv[1], - argv[2], - NULL, - NULL, - &standard_request_handler, - NULL, - NULL, - SPDY_DAEMON_OPTION_SESSION_TIMEOUT, - 1800, - SPDY_DAEMON_OPTION_END); - - if(NULL==daemon){ - printf("no daemon\n"); - return 1; - } - - basedir = argv[3]; - - do - { - FD_ZERO(&read_fd_set); - FD_ZERO(&write_fd_set); - FD_ZERO(&except_fd_set); - - ret = SPDY_get_timeout(daemon, &timeoutlong); - if(SPDY_NO == ret || timeoutlong > 1000) - { - timeout.tv_sec = 1; - timeout.tv_usec = 0; - } - else - { - timeout.tv_sec = timeoutlong / 1000; - timeout.tv_usec = (timeoutlong % 1000) * 1000; - } - - maxfd = SPDY_get_fdset (daemon, - &read_fd_set, - &write_fd_set, - &except_fd_set); - - ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); - - switch(ret) { - case -1: - printf("select error: %i\n", errno); - break; - case 0: - - break; - default: - SPDY_run(daemon); - - break; - } - } - while(run); - - SPDY_stop_daemon(daemon); - - SPDY_deinit(); - - return 0; -} - diff --git a/src/examples/spdy_response_with_callback.c b/src/examples/spdy_response_with_callback.c @@ -1,236 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file response_with_callback.c - * @brief shows how to create responses with callbacks - * @author Andrey Uzunov - */ - -//for asprintf -#define _GNU_SOURCE - -#include <unistd.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <string.h> -#include <stdio.h> -#include <ctype.h> -#include <errno.h> -#include "microspdy.h" - -static int run = 1; - - -static ssize_t -response_callback (void *cls, - void *buffer, - size_t max, - bool *more) -{ - FILE *fd =(FILE*)cls; - - int ret = fread(buffer,1,max,fd); - *more = feof(fd) == 0; - - if(!(*more)) - fclose(fd); - - return ret; -} - - -static void -response_done_callback(void *cls, - struct SPDY_Response *response, - struct SPDY_Request *request, - enum SPDY_RESPONSE_RESULT status, - bool streamopened) -{ - (void)streamopened; - (void)status; - - printf("answer for %s was sent\n", (char *)cls); - - SPDY_destroy_request(request); - SPDY_destroy_response(response); - free(cls); -} - - -static void -standard_request_handler(void *cls, - struct SPDY_Request * request, - uint8_t priority, - const char *method, - const char *path, - const char *version, - const char *host, - const char *scheme, - struct SPDY_NameValue * headers, - bool more) -{ - (void)cls; - (void)request; - (void)priority; - (void)host; - (void)scheme; - (void)headers; - (void)more; - - char *html; - struct SPDY_Response *response=NULL; - struct SPDY_NameValue *resp_headers; - - printf("received request for '%s %s %s'\n", method, path, version); - if(strcmp(path,"/spdy-draft.txt")==0) - { - FILE *fd = fopen(DATA_DIR "spdy-draft.txt","r"); - - if(NULL == (resp_headers = SPDY_name_value_create())) - { - fprintf(stdout,"SPDY_name_value_create failed\n"); - abort(); - } - if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_TYPE,"text/plain")) - { - fprintf(stdout,"SPDY_name_value_add failed\n"); - abort(); - } - - response = SPDY_build_response_with_callback(200,NULL, - SPDY_HTTP_VERSION_1_1,resp_headers,&response_callback,fd,SPDY_MAX_SUPPORTED_FRAME_SIZE); - SPDY_name_value_destroy(resp_headers); - } - else - { - if(strcmp(path,"/close")==0) - { - asprintf(&html,"<html>" - "<body><b>Closing now!</body></html>"); - run = 0; - } - else - { - asprintf(&html,"<html>" - "<body><a href=\"/spdy-draft.txt\">/spdy-draft.txt</a><br></body></html>"); - } - - response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,html,strlen(html)); - free(html); - } - - if(NULL==response){ - fprintf(stdout,"no response obj\n"); - abort(); - } - - void *clspath = strdup(path); - - if(SPDY_queue_response(request,response,true,false,&response_done_callback,clspath)!=SPDY_YES) - { - fprintf(stdout,"queue\n"); - abort(); - } -} - - -int -main (int argc, char *const *argv) -{ - unsigned long long timeoutlong=0; - struct timeval timeout; - int ret; - fd_set read_fd_set; - fd_set write_fd_set; - fd_set except_fd_set; - int maxfd = -1; - struct SPDY_Daemon *daemon; - - if(argc != 2) - { - return 1; - } - - SPDY_init(); - - daemon = SPDY_start_daemon(atoi(argv[1]), - DATA_DIR "cert-and-key.pem", - DATA_DIR "cert-and-key.pem", - NULL, - NULL, - &standard_request_handler, - NULL, - NULL, - SPDY_DAEMON_OPTION_SESSION_TIMEOUT, - 1800, - SPDY_DAEMON_OPTION_END); - - if(NULL==daemon){ - printf("no daemon\n"); - return 1; - } - - do - { - FD_ZERO(&read_fd_set); - FD_ZERO(&write_fd_set); - FD_ZERO(&except_fd_set); - - ret = SPDY_get_timeout(daemon, &timeoutlong); - if(SPDY_NO == ret || timeoutlong > 1000) - { - timeout.tv_sec = 1; - timeout.tv_usec = 0; - } - else - { - timeout.tv_sec = timeoutlong / 1000; - timeout.tv_usec = (timeoutlong % 1000) * 1000; - } - - maxfd = SPDY_get_fdset (daemon, - &read_fd_set, - &write_fd_set, - &except_fd_set); - - ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); - - switch(ret) { - case -1: - printf("select error: %i\n", errno); - break; - case 0: - - break; - default: - SPDY_run(daemon); - - break; - } - } - while(run); - - SPDY_stop_daemon(daemon); - - SPDY_deinit(); - - return 0; -} - diff --git a/src/include/Makefile.am b/src/include/Makefile.am @@ -1,10 +1,6 @@ # This Makefile.am is in the public domain SUBDIRS = . -if ENABLE_SPDY -microspdy = microspdy.h -endif - -include_HEADERS = microhttpd.h $(microspdy) +include_HEADERS = microhttpd.h EXTRA_DIST = platform.h platform_interface.h w32functions.h autoinit_funcs.h diff --git a/src/include/microspdy.h b/src/include/microspdy.h @@ -1,1380 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012, 2013 Christian Grothoff - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file microspdy.h - * @brief public interface to libmicrospdy - * @author Andrey Uzunov - * @author Christian Grothoff - * - * All symbols defined in this header start with SPDY_. libmisrospdy is a small - * SPDY daemon library. The application can start multiple daemons - * and they are independent.<p> - * - * The header file defines various constants used by the SPDY and the HTTP protocol. - * This does not mean that the lib actually interprets all of these - * values. Not everything is implemented. The provided constants are exported as a convenience - * for users of the library. The lib does not verify that provided - * HTTP headers and if their values conform to the SPDY protocol, - * it only checks if the required headers for the SPDY requests and - * responses are provided.<p> - * - * The library uses just a single thread.<p> - * - * Before including "microspdy.h" you should add the necessary - * includes to define the types used in this file (which headers are needed may - * depend on your platform; for possible suggestions consult - * "platform.h" in the libmicrospdy distribution).<p> - * - * All of the functions returning SPDY_YES/SPDY_NO return - * SPDY_INPUT_ERROR when any of the parameters are invalid, e.g. - * required parameter is NULL.<p> - * - * The library does not check if anything at the application layer -- - * requests and responses -- is correct. For example, it - * is up to the user to check if a client is sending HTTP body but the - * method is GET.<p> - * - * The SPDY flow control is just partially implemented: the receiving - * window is updated, and the client is notified, to prevent a client - * from stop sending POST body data, for example. - */ -#ifndef SPDY_MICROSPDY_H -#define SPDY_MICROSPDY_H - -#include <zlib.h> -#include <stdbool.h> - -/* While we generally would like users to use a configure-driven - build process which detects which headers are present and - hence works on any platform, we use "standard" includes here - to build out-of-the-box for beginning users on common systems. - - Once you have a proper build system and go for more exotic - platforms, you should define MHD_PLATFORM_H in some header that - you always include *before* "microhttpd.h". Then the following - "standard" includes won't be used (which might be a good - idea, especially on platforms where they do not exist). */ -#ifndef MHD_PLATFORM_H -#include <unistd.h> -#include <stdarg.h> -#include <stdint.h> -#ifdef __MINGW32__ -#include <ws2tcpip.h> -#else -#include <sys/time.h> -#include <sys/types.h> -#include <sys/socket.h> -#endif -#endif - -#ifndef _MHD_EXTERN -#define _MHD_EXTERN extern -#endif - -/** - * return code for "YES". - */ -#define SPDY_YES 1 - -/** - * return code for "NO". - */ -#define SPDY_NO 0 - -/** - * return code for error when input parameters are wrong. To be returned - * only by functions which return int. The others will return NULL on - * input error. - */ -#define SPDY_INPUT_ERROR -1 - -/** - * SPDY version supported by the lib. - */ -#define SPDY_VERSION 3 - -/** - * The maximum allowed size (without 8 byte headers) of - * SPDY frames (value length) is 8192. The lib will accept and - * send frames with length at most this value here. - */ -#define SPDY_MAX_SUPPORTED_FRAME_SIZE 8192 - -/** - * HTTP response codes. - */ -#define SPDY_HTTP_CONTINUE 100 -#define SPDY_HTTP_SWITCHING_PROTOCOLS 101 -#define SPDY_HTTP_PROCESSING 102 - -#define SPDY_HTTP_OK 200 -#define SPDY_HTTP_CREATED 201 -#define SPDY_HTTP_ACCEPTED 202 -#define SPDY_HTTP_NON_AUTHORITATIVE_INFORMATION 203 -#define SPDY_HTTP_NO_CONTENT 204 -#define SPDY_HTTP_RESET_CONTENT 205 -#define SPDY_HTTP_PARTIAL_CONTENT 206 -#define SPDY_HTTP_MULTI_STATUS 207 - -#define SPDY_HTTP_MULTIPLE_CHOICES 300 -#define SPDY_HTTP_MOVED_PERMANENTLY 301 -#define SPDY_HTTP_FOUND 302 -#define SPDY_HTTP_SEE_OTHER 303 -#define SPDY_HTTP_NOT_MODIFIED 304 -#define SPDY_HTTP_USE_PROXY 305 -#define SPDY_HTTP_SWITCH_PROXY 306 -#define SPDY_HTTP_TEMPORARY_REDIRECT 307 - -#define SPDY_HTTP_BAD_REQUEST 400 -#define SPDY_HTTP_UNAUTHORIZED 401 -#define SPDY_HTTP_PAYMENT_REQUIRED 402 -#define SPDY_HTTP_FORBIDDEN 403 -#define SPDY_HTTP_NOT_FOUND 404 -#define SPDY_HTTP_METHOD_NOT_ALLOWED 405 -#define SPDY_HTTP_METHOD_NOT_ACCEPTABLE 406 -#define SPDY_HTTP_PROXY_AUTHENTICATION_REQUIRED 407 -#define SPDY_HTTP_REQUEST_TIMEOUT 408 -#define SPDY_HTTP_CONFLICT 409 -#define SPDY_HTTP_GONE 410 -#define SPDY_HTTP_LENGTH_REQUIRED 411 -#define SPDY_HTTP_PRECONDITION_FAILED 412 -#define SPDY_HTTP_REQUEST_ENTITY_TOO_LARGE 413 -#define SPDY_HTTP_REQUEST_URI_TOO_LONG 414 -#define SPDY_HTTP_UNSUPPORTED_MEDIA_TYPE 415 -#define SPDY_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE 416 -#define SPDY_HTTP_EXPECTATION_FAILED 417 -#define SPDY_HTTP_UNPROCESSABLE_ENTITY 422 -#define SPDY_HTTP_LOCKED 423 -#define SPDY_HTTP_FAILED_DEPENDENCY 424 -#define SPDY_HTTP_UNORDERED_COLLECTION 425 -#define SPDY_HTTP_UPGRADE_REQUIRED 426 -#define SPDY_HTTP_NO_RESPONSE 444 -#define SPDY_HTTP_RETRY_WITH 449 -#define SPDY_HTTP_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS 450 -#define SPDY_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS 451 - -#define SPDY_HTTP_INTERNAL_SERVER_ERROR 500 -#define SPDY_HTTP_NOT_IMPLEMENTED 501 -#define SPDY_HTTP_BAD_GATEWAY 502 -#define SPDY_HTTP_SERVICE_UNAVAILABLE 503 -#define SPDY_HTTP_GATEWAY_TIMEOUT 504 -#define SPDY_HTTP_HTTP_VERSION_NOT_SUPPORTED 505 -#define SPDY_HTTP_VARIANT_ALSO_NEGOTIATES 506 -#define SPDY_HTTP_INSUFFICIENT_STORAGE 507 -#define SPDY_HTTP_BANDWIDTH_LIMIT_EXCEEDED 509 -#define SPDY_HTTP_NOT_EXTENDED 510 - -/** - * HTTP headers are used in SPDY, but all of them MUST be lowercase. - * Some are not valid in SPDY and MUST not be used - */ -#define SPDY_HTTP_HEADER_ACCEPT "accept" -#define SPDY_HTTP_HEADER_ACCEPT_CHARSET "accept-charset" -#define SPDY_HTTP_HEADER_ACCEPT_ENCODING "accept-encoding" -#define SPDY_HTTP_HEADER_ACCEPT_LANGUAGE "accept-language" -#define SPDY_HTTP_HEADER_ACCEPT_RANGES "accept-ranges" -#define SPDY_HTTP_HEADER_AGE "age" -#define SPDY_HTTP_HEADER_ALLOW "allow" -#define SPDY_HTTP_HEADER_AUTHORIZATION "authorization" -#define SPDY_HTTP_HEADER_CACHE_CONTROL "cache-control" -/* Connection header is forbidden in SPDY */ -#define SPDY_HTTP_HEADER_CONNECTION "connection" -#define SPDY_HTTP_HEADER_CONTENT_ENCODING "content-encoding" -#define SPDY_HTTP_HEADER_CONTENT_LANGUAGE "content-language" -#define SPDY_HTTP_HEADER_CONTENT_LENGTH "content-length" -#define SPDY_HTTP_HEADER_CONTENT_LOCATION "content-location" -#define SPDY_HTTP_HEADER_CONTENT_MD5 "content-md5" -#define SPDY_HTTP_HEADER_CONTENT_RANGE "content-range" -#define SPDY_HTTP_HEADER_CONTENT_TYPE "content-type" -#define SPDY_HTTP_HEADER_COOKIE "cookie" -#define SPDY_HTTP_HEADER_DATE "date" -#define SPDY_HTTP_HEADER_ETAG "etag" -#define SPDY_HTTP_HEADER_EXPECT "expect" -#define SPDY_HTTP_HEADER_EXPIRES "expires" -#define SPDY_HTTP_HEADER_FROM "from" -/* Host header is forbidden in SPDY */ -#define SPDY_HTTP_HEADER_HOST "host" -#define SPDY_HTTP_HEADER_IF_MATCH "if-match" -#define SPDY_HTTP_HEADER_IF_MODIFIED_SINCE "if-modified-since" -#define SPDY_HTTP_HEADER_IF_NONE_MATCH "if-none-match" -#define SPDY_HTTP_HEADER_IF_RANGE "if-range" -#define SPDY_HTTP_HEADER_IF_UNMODIFIED_SINCE "if-unmodified-since" -/* Keep-Alive header is forbidden in SPDY */ -#define SPDY_HTTP_HEADER_KEEP_ALIVE "keep-alive" -#define SPDY_HTTP_HEADER_LAST_MODIFIED "last-modified" -#define SPDY_HTTP_HEADER_LOCATION "location" -#define SPDY_HTTP_HEADER_MAX_FORWARDS "max-forwards" -#define SPDY_HTTP_HEADER_PRAGMA "pragma" -#define SPDY_HTTP_HEADER_PROXY_AUTHENTICATE "proxy-authenticate" -#define SPDY_HTTP_HEADER_PROXY_AUTHORIZATION "proxy-authorization" -/* Proxy-Connection header is forbidden in SPDY */ -#define SPDY_HTTP_HEADER_PROXY_CONNECTION "proxy-connection" -#define SPDY_HTTP_HEADER_RANGE "range" -#define SPDY_HTTP_HEADER_REFERER "referer" -#define SPDY_HTTP_HEADER_RETRY_AFTER "retry-after" -#define SPDY_HTTP_HEADER_SERVER "server" -#define SPDY_HTTP_HEADER_SET_COOKIE "set-cookie" -#define SPDY_HTTP_HEADER_SET_COOKIE2 "set-cookie2" -#define SPDY_HTTP_HEADER_TE "te" -#define SPDY_HTTP_HEADER_TRAILER "trailer" -/* Transfer-Encoding header is forbidden in SPDY */ -#define SPDY_HTTP_HEADER_TRANSFER_ENCODING "transfer-encoding" -#define SPDY_HTTP_HEADER_UPGRADE "upgrade" -#define SPDY_HTTP_HEADER_USER_AGENT "user-agent" -#define SPDY_HTTP_HEADER_VARY "vary" -#define SPDY_HTTP_HEADER_VIA "via" -#define SPDY_HTTP_HEADER_WARNING "warning" -#define SPDY_HTTP_HEADER_WWW_AUTHENTICATE "www-authenticate" - -/** - * HTTP versions (a value must be provided in SPDY requests/responses). - */ -#define SPDY_HTTP_VERSION_1_0 "HTTP/1.0" -#define SPDY_HTTP_VERSION_1_1 "HTTP/1.1" - -/** - * HTTP methods - */ -#define SPDY_HTTP_METHOD_CONNECT "CONNECT" -#define SPDY_HTTP_METHOD_DELETE "DELETE" -#define SPDY_HTTP_METHOD_GET "GET" -#define SPDY_HTTP_METHOD_HEAD "HEAD" -#define SPDY_HTTP_METHOD_OPTIONS "OPTIONS" -#define SPDY_HTTP_METHOD_POST "POST" -#define SPDY_HTTP_METHOD_PUT "PUT" -#define SPDY_HTTP_METHOD_TRACE "TRACE" - -/** - * HTTP POST encodings, see also - * http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 - */ -#define SPDY_HTTP_POST_ENCODING_FORM_URLENCODED "application/x-www-form-urlencoded" -#define SPDY_HTTP_POST_ENCODING_MULTIPART_FORMDATA "multipart/form-data" - - -/** - * Handle for the daemon (listening on a socket). - */ -struct SPDY_Daemon; - - -/** - * Handle for a SPDY session/connection. - */ -struct SPDY_Session; - - -/** - * Handle for a SPDY request sent by a client. The structure has pointer - * to the session's handler - */ -struct SPDY_Request; - - -/** - * Handle for a response containing HTTP headers and data to be sent. - * The structure has pointer to the session's handler - * for this response. - */ -struct SPDY_Response; - - -/** - * Collection of tuples of an HTTP header and values used in requests - * and responses. - */ -struct SPDY_NameValue; - - -/** - * Collection of tuples of a SPDY setting ID, value - * and flags used to control the sessions. - */ -struct SPDY_Settings; - - -/** - * SPDY IO sybsystem flags used by SPDY_init() and SPDY_deinit().<p> - * - * The values are used internally as flags, that is why they must be - * powers of 2. - */ -enum SPDY_IO_SUBSYSTEM -{ - - /** - * No subsystem. For internal use. - */ - SPDY_IO_SUBSYSTEM_NONE = 0, - - /** - * Default TLS implementation provided by openSSL/libssl. - */ - SPDY_IO_SUBSYSTEM_OPENSSL = 1, - - /** - * No TLS is used. - */ - SPDY_IO_SUBSYSTEM_RAW = 2 -}; - - -/** - * SPDY daemon options. Passed in the varargs portion of - * SPDY_start_daemon to customize the daemon. Each option must - * be followed by a value of a specific type.<p> - * - * The values are used internally as flags, that is why they must be - * powers of 2. - */ -enum SPDY_DAEMON_OPTION -{ - - /** - * No more options / last option. This is used - * to terminate the VARARGs list. - */ - SPDY_DAEMON_OPTION_END = 0, - - /** - * Set a custom timeout for all connections. Must be followed by - * a number of seconds, given as an 'unsigned int'. Use - * zero for no timeout. - */ - SPDY_DAEMON_OPTION_SESSION_TIMEOUT = 1, - - /** - * Bind daemon to the supplied sockaddr. This option must be - * followed by a 'struct sockaddr *'. The 'struct sockaddr*' - * should point to a 'struct sockaddr_in6' or to a - * 'struct sockaddr_in'. - */ - SPDY_DAEMON_OPTION_SOCK_ADDR = 2, - - /** - * Flags for the daemon. Must be followed by a SPDY_DAEMON_FLAG value - * which is the result of bitwise OR of desired flags. - */ - SPDY_DAEMON_OPTION_FLAGS = 4, - - /** - * IO subsystem type used by daemon and all its sessions. If not set, - * TLS provided by openssl is used. Must be followed by a - * SPDY_IO_SUBSYSTEM value. - */ - SPDY_DAEMON_OPTION_IO_SUBSYSTEM = 8, - - /** - * Maximum number of frames to be written to the socket at once. The - * library tries to send max_num_frames in a single call to SPDY_run - * for a single session. This means no requests can be received nor - * other sessions can send data as long the current one has enough - * frames to send and there is no error on writing. Thus, a big value - * will affect the performance. Small value gives fairnes for sessions. - * Must be followed by a positive integer (uin32_t). If not set, the - * default value 10 will be used. - */ - SPDY_DAEMON_OPTION_MAX_NUM_FRAMES = 16 -}; - - -/** - * Flags for starting SPDY daemon. They are used to set some settings - * for the daemon, which do not require values. - */ -enum SPDY_DAEMON_FLAG -{ - /** - * No flags selected. - */ - SPDY_DAEMON_FLAG_NO = 0, - - /** - * The server will bind only on IPv6 addresses. If the flag is set and - * the daemon is provided with IPv4 address or IPv6 is not supported, - * starting daemon will fail. - */ - SPDY_DAEMON_FLAG_ONLY_IPV6 = 1, - - /** - * All sessions' sockets will be set with TCP_NODELAY if the flag is - * used. Option considered only by SPDY_IO_SUBSYSTEM_RAW. - */ - SPDY_DAEMON_FLAG_NO_DELAY = 2 -}; - - -/** - * SPDY settings IDs sent by both client and server in SPDY SETTINGS frame. - * They affect the whole SPDY session. Defined in SPDY Protocol - Draft 3. - */ -enum SPDY_SETTINGS -{ - - /** - * Allows the sender to send its expected upload bandwidth on this - * channel. This number is an estimate. The value should be the - * integral number of kilobytes per second that the sender predicts - * as an expected maximum upload channel capacity. - */ - SPDY_SETTINGS_UPLOAD_BANDWIDTH = 1, - - /** - * Allows the sender to send its expected download bandwidth on this - * channel. This number is an estimate. The value should be the - * integral number of kilobytes per second that the sender predicts as - * an expected maximum download channel capacity. - */ - SPDY_SETTINGS_DOWNLOAD_BANDWIDTH = 2, - - /** - * Allows the sender to send its expected round-trip-time on this - * channel. The round trip time is defined as the minimum amount of - * time to send a control frame from this client to the remote and - * receive a response. The value is represented in milliseconds. - */ - SPDY_SETTINGS_ROUND_TRIP_TIME = 3, - - /** - * Allows the sender to inform the remote endpoint the maximum number - * of concurrent streams which it will allow. By default there is no - * limit. For implementors it is recommended that this value be no - * smaller than 100. - */ - SPDY_SETTINGS_MAX_CONCURRENT_STREAMS = 4, - - /** - * Allows the sender to inform the remote endpoint of the current TCP - * CWND value. - */ - SPDY_SETTINGS_CURRENT_CWND = 5, - - /** - * Allows the sender to inform the remote endpoint the retransmission - * rate (bytes retransmitted / total bytes transmitted). - */ - SPDY_SETTINGS_DOWNLOAD_RETRANS_RATE = 6, - - /** - * Allows the sender to inform the remote endpoint the initial window - * size (in bytes) for new streams. - */ - SPDY_SETTINGS_INITIAL_WINDOW_SIZE = 7, - - /** - * Allows the server to inform the client if the new size of the - * client certificate vector. - */ - SPDY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8 -}; - - -/** - * Flags for each individual SPDY setting in the SPDY SETTINGS frame. - * They affect only one setting to which they are set. - * Defined in SPDY Protocol - Draft 3. - */ -enum SPDY_FLAG_SETTINGS -{ - - /** - * When set, the sender of this SETTINGS frame is requesting that the - * recipient persist the ID/Value and return it in future SETTINGS - * frames sent from the sender to this recipient. Because persistence - * is only implemented on the client, this flag is only sent by the - * server. - */ - SPDY_FLAG_SETTINGS_PERSIST_VALUE = 1, - - /** - * When set, the sender is notifying the recipient that this ID/Value - * pair was previously sent to the sender by the recipient with the - * #SPDY_FLAG_SETTINGS_PERSIST_VALUE, and the sender is returning it. - * Because persistence is only implemented on the client, this flag is - * only sent by the client. - */ - SPDY_FLAG_SETTINGS_PERSISTED = 2 -}; - - -/** - * Flag associated with a whole SPDY SETTINGS frame. Affect all the - * settings in the frame. Defined in SPDY Protocol - Draft 3. - */ -enum SPDY_FLAG_SETTINGS_FRAME -{ - - /** - * When set, the client should clear any previously persisted SETTINGS - * ID/Value pairs. If this frame contains ID/Value pairs with the - * #SPDY_FLAG_SETTINGS_PERSIST_VALUE set, then the client will first - * clear its existing, persisted settings, and then persist the values - * with the flag set which are contained within this frame. Because - * persistence is only implemented on the client, this flag can only - * be used when the sender is the server. - */ - SPDY_FLAG_SETTINGS_CLEAR_SETTINGS = 1 -}; - - -/** - * SPDY settings function options. Passed in the varargs portion of - * SPDY_SettingsReceivedCallback and SPDY_send_settings to customize - * more the settings handling. Each option must - * be followed by a value of a specific type.<p> - * - * The values are used internally as flags, that is why they must be - * powers of 2. - */ -enum SPDY_SETTINGS_OPTION -{ - - /** - * No more options / last option. This is used - * to terminate the VARARGs list. - */ - SPDY_SETTINGS_OPTION_END = 0 -}; - - -/** - * Used as a parameter for SPDY_ResponseResultCallback and shows if the - * response was actually written to the TLS socket or discarded by the - * lib for any reason (and respectively the reason). - */ -enum SPDY_RESPONSE_RESULT -{ - - /** - * The lib has written the full response to the TLS socket. - */ - SPDY_RESPONSE_RESULT_SUCCESS = 0, - - /** - * The session is being closed, so the data is being discarded - */ - SPDY_RESPONSE_RESULT_SESSION_CLOSED = 1, - - /** - * The stream for this response has been closed. May happen when the - * sender had sent first SYN_STREAM and after that RST_STREAM. - */ - SPDY_RESPONSE_RESULT_STREAM_CLOSED = 2 -}; - - -/** - * Callback for serious error condition. The default action is to print - * an error message and abort(). - * - * @param cls user specified value - * @param file where the error occured - * @param line where the error occured - * @param reason error details message, may be NULL - */ -typedef void -(*SPDY_PanicCallback) (void * cls, - const char *file, - unsigned int line, - const char *reason); - - -/** - * Callback for new SPDY session established by a client. Called - * immediately after the TCP connection was established. - * - * @param cls client-defined closure - * @param session handler for the new SPDY session - */ -typedef void -(*SPDY_NewSessionCallback) (void * cls, - struct SPDY_Session * session); - - -/** - * Callback for closed session. Called after the TCP connection was - * closed. In this callback function the user has the last - * chance to access the SPDY_Session structure. After that the latter - * will be cleaned! - * - * @param cls client-defined closure - * @param session handler for the closed SPDY session - * @param by_client #SPDY_YES if the session close was initiated by the - * client; - * #SPDY_NO if closed by the server - */ -typedef void -(*SPDY_SessionClosedCallback) (void *cls, - struct SPDY_Session *session, - int by_client); - - -/** - * Iterator over name-value pairs. - * - * @param cls client-defined closure - * @param name of the pair - * @param value of the pair - * @return #SPDY_YES to continue iterating, - * #SPDY_NO to abort the iteration - */ -typedef int -(*SPDY_NameValueIterator) (void *cls, - const char *name, - const char * const * value, - int num_values); - - -/** - * Callback for received SPDY request. The functions is called whenever - * a reqest comes, but will also be called if more headers/trailers are - * received. - * - * @param cls client-defined closure - * @param request handler. The request object is required for - * sending responses. - * @param priority of the SPDY stream which the request was - * sent over - * @param method HTTP method - * @param path HTTP path - * @param version HTTP version just like in HTTP request/response: - * "HTTP/1.0" or "HTTP/1.1" currently - * @param host called host as in HTTP - * @param scheme used ("http" or "https"). In SPDY 3 it is only "https". - * @param headers other HTTP headers from the request - * @param more a flag saying if more data related to the request is - * expected to be received. HTTP body may arrive (e.g. POST data); - * then SPDY_NewDataCallback will be called for the connection. - * It is also possible that more headers/trailers arrive; - * then the same callback will be invoked. The user should detect - * that it is not the first invocation of the function for that - * request. - */ -typedef void -(*SPDY_NewRequestCallback) (void *cls, - struct SPDY_Request *request, - uint8_t priority, - const char *method, - const char *path, - const char *version, - const char *host, - const char *scheme, - struct SPDY_NameValue *headers, - bool more); - - -/** - * Callback for received new data chunk (HTTP body) from a given - * request (e.g. POST data). - * - * @param cls client-defined closure - * @param request handler - * @param buf data chunk from the POST data - * @param size the size of the data chunk 'buf' in bytes. Note that it - * may be 0. - * @param more false if this is the last chunk from the data. Note: - * true does not mean that more data will come, exceptional - * situation is possible - * @return #SPDY_YES to continue calling the function, - * #SPDY_NO to stop calling the function for this request - */ -typedef int -(*SPDY_NewDataCallback) (void *cls, - struct SPDY_Request *request, - const void *buf, - size_t size, - bool more); -// How about passing POST encoding information -// here as well? -//TODO - - -/** - * Callback to be used with SPDY_build_response_with_callback. The - * callback will be called when the lib wants to write to the TLS socket. - * The application should provide the data to be sent. - * - * @param cls client-defined closure - * @param max maximum number of bytes that are allowed to be written - * to the buffer. - * @param more true if more data will be sent (i.e. the function must - * be calleed again), - * false if this is the last chunk, the lib will close - * the stream - * @return number of bytes written to buffer. On error the call MUST - * return value less than 0 to indicate the library. - */ -typedef ssize_t -(*SPDY_ResponseCallback) (void *cls, - void *buffer, - size_t max, - bool *more); - - -/** - * Callback to be called when the last bytes from the response was sent - * to the client or when the response was discarded from the lib. This - * callback is a very good place to discard the request and the response - * objects, if they will not be reused (e.g., sending the same response - * again). If the stream is closed it is safe to discard the request - * object. - * - * @param cls client-defined closure - * @param response handler to the response that was just sent - * @param request handler to the request for which the response was sent - * @param status shows if actually the response was sent or it was - * discarded by the lib for any reason (e.g., closing session, - * closing stream, stopping daemon, etc.). It is possible that - * status indicates an error but parts of the response headers - * and/or body (in one - * or several frames) were already sent to the client. - * @param streamopened indicates if the the stream for this request/ - * response pair is still opened. If yes, the server may want - * to use SPDY push to send something additional to the client - * and/or close the stream. - */ -typedef void -(*SPDY_ResponseResultCallback) (void * cls, - struct SPDY_Response *response, - struct SPDY_Request *request, - enum SPDY_RESPONSE_RESULT status, - bool streamopened); - - -/** - * Callback to notify when SPDY ping response is received. - * - * @param session handler for which the ping request was sent - * @param rtt the timespan between sending ping request and receiving it - * from the library - */ -typedef void -(*SPDY_PingCallback) (void * cls, - struct SPDY_Session *session, - struct timeval *rtt); - - -/** - * Iterator over settings ID/Value/Flags tuples. - * - * @param cls client-defined closure - * @param id SPDY settings ID - * @param value value for this setting - * @param flags flags for this tuple; use - * `enum SPDY_FLAG_SETTINGS` - * @return #SPDY_YES to continue iterating, - * #SPDY_NO to abort the iteration - */ -typedef int -(*SPDY_SettingsIterator) (void *cls, - enum SPDY_SETTINGS id, - int32_t value, - uint8_t flags); - - -/** - * Callback to notify when SPDY SETTINGS are received from the client. - * - * @param session handler for which settings are received - * @param settings ID/value/flags tuples of the settings - * @param flags for the whole settings frame; use - * enum SPDY_FLAG_SETTINGS_FRAME - * @param ... list of options (type-value pairs, - * terminated with #SPDY_SETTINGS_OPTION_END). - */ -typedef void -(*SPDY_SettingsReceivedCallback) (struct SPDY_Session *session, - struct SPDY_Settings *settings, - uint8_t flags, - ...); - - -/* Global functions for the library */ - - -/** - * Init function for the whole library. It MUST be called before any - * other function of the library to initialize things like TLS context - * and possibly other stuff needed by the lib. Currently the call - * always returns #SPDY_YES. - * - * @param io_subsystem the IO subsystem that will - * be initialized. Several can be used with bitwise OR. If no - * parameter is set, the default openssl subsystem will be used. - * @return #SPDY_YES if the library was correctly initialized and its - * functions can be used now; - * #SPDY_NO on error - */ -_MHD_EXTERN int -(SPDY_init) (enum SPDY_IO_SUBSYSTEM io_subsystem, ...); -#define SPDY_init() SPDY_init (SPDY_IO_SUBSYSTEM_OPENSSL) - - -/** - * Deinit function for the whole lib. It can be called after finishing - * using the library. It frees and cleans up resources allocated in - * SPDY_init. Currently the function does not do anything. - */ -_MHD_EXTERN void -SPDY_deinit (void); - - -/** - * Sets the global error handler to a different implementation. "cb" - * will only be called in the case of typically fatal, serious - * internal consistency issues. These issues should only arise in the - * case of serious memory corruption or similar problems with the - * architecture as well as failed assertions. While "cb" is allowed to - * return and the lib will then try to continue, this is never safe. - * - * The default implementation that is used if no panic function is set - * simply prints an error message and calls "abort". Alternative - * implementations might call "exit" or other similar functions. - * - * @param cb new error handler - * @param cls passed to error handler - */ -_MHD_EXTERN void -SPDY_set_panic_func (SPDY_PanicCallback cb, - void *cls); - - -/* Daemon functions */ - - -/** - * Start a SPDY webserver on the given port. - * - * @param port to bind to. The value is ignored if address structure - * is passed as daemon option - * @param certfile path to the certificate that will be used by server - * @param keyfile path to the keyfile for the certificate - * @param nscb callback called when a new SPDY session is - * established by a client - * @param sccb callback called when a session is closed - * @param nrcb callback called when a client sends request - * @param npdcb callback called when HTTP body (POST data) is received - * after request - * @param cls common extra argument to all of the callbacks - * @param ... list of options (type-value pairs, - * terminated with #SPDY_DAEMON_OPTION_END). - * @return NULL on error, handle to daemon on success - */ -_MHD_EXTERN struct SPDY_Daemon * -SPDY_start_daemon (uint16_t port, - const char *certfile, - const char *keyfile, - SPDY_NewSessionCallback nscb, - SPDY_SessionClosedCallback sccb, - SPDY_NewRequestCallback nrcb, - SPDY_NewDataCallback npdcb, - void *cls, - ...); - - -/** - * Shutdown the daemon. First all sessions are closed. It is NOT safe - * to call this function in user callbacks. - * - * @param daemon to stop - */ -_MHD_EXTERN void -SPDY_stop_daemon (struct SPDY_Daemon *daemon); - - -/** - * Obtain the select sets for this daemon. Only those are retrieved, - * which some processing should be done for, i.e. not all sockets are - * added to write_fd_set.<p> - * - * It is possible that there is - * nothing to be read from a socket but there is data either in the - * TLS subsystem's read buffers or in libmicrospdy's read buffers, which - * waits for being processed. In such case the file descriptor will be - * added to write_fd_set. Since it is very likely for the socket to be - * ready for writing, the select used in the application's event loop - * will return with success, SPDY_run will be called, the data will be - * processed and maybe something will be written to the socket. Without - * this behaviour, considering a proper event loop, data may stay in the - * buffers, but run is never called. - * - * @param daemon to get sets from - * @param read_fd_set read set - * @param write_fd_set write set - * @param except_fd_set except set - * @return largest FD added to any of the sets - */ -_MHD_EXTERN int -SPDY_get_fdset (struct SPDY_Daemon *daemon, - fd_set *read_fd_set, - fd_set *write_fd_set, - fd_set *except_fd_set); - - -/** - * Obtain timeout value for select for this daemon. The returned value - * is how long select - * should at most block, not the timeout value set for connections. - * - * @param daemon to query for timeout - * @param timeout will be set to the timeout value (in milliseconds) - * @return #SPDY_YES on success - * #SPDY_NO if no connections exist that - * would necessiate the use of a timeout right now - */ -_MHD_EXTERN int -SPDY_get_timeout (struct SPDY_Daemon *daemon, - unsigned long long *timeout); - - -/** - * Run webserver operations. This method must be called in - * the client event loop. - * - * @param daemon to run - */ -_MHD_EXTERN void -SPDY_run (struct SPDY_Daemon *daemon); - - -/* SPDY Session handling functions */ - - -/** - * Closes a SPDY session. SPDY clients and servers are expected to keep - * sessions opened as long as possible. However, the server may want to - * close some connections, e.g. if there are too many, to free some - * resources. The function can also be used to close a specific session - * if the client is not desired. - * - * @param session handler to be closed - */ -_MHD_EXTERN void -SPDY_close_session (struct SPDY_Session * session); - - -/** - * Associate a void pointer with a session. The data accessible by the - * pointer can later be used wherever the session handler is available. - * - * @param session handler - * @param cls any data pointed by a pointer to be accessible later - */ -_MHD_EXTERN void -SPDY_set_cls_to_session (struct SPDY_Session *session, - void *cls); - - -/** - * Retrieves the pointer associated with SPDY_set_cls_to_session(). - * - * @param session handler to get its cls - * @return same pointer added by SPDY_set_cls_to_session() or - * NULL when nothing was associated - */ -_MHD_EXTERN void * -SPDY_get_cls_from_session (struct SPDY_Session *session); - - -/** - * Retrieves the remote address of a given session. - * - * @param session handler to get its remote address - * @param addr out parameter; pointing to remote address - * @return length of the address structure - */ -_MHD_EXTERN socklen_t -SPDY_get_remote_addr (struct SPDY_Session *session, - struct sockaddr **addr); - - -/* SPDY name/value data structure handling functions */ - - -/** - * Create a new NameValue structure. It is needed for putting inside the - * HTTP headers and their values for a response. The user should later - * destroy alone the structure. - * - * @return handler to the new empty structure or NULL on error - */ -_MHD_EXTERN struct SPDY_NameValue * -SPDY_name_value_create (void); - - -/** - * Add name/value pair to a NameValue structure. SPDY_NO will be returned - * if the name/value pair is already in the structure. It is legal to - * add different values for the same name. - * - * @param container structure to which the new pair is added - * @param name for the value. Null-terminated string. - * @param value the value itself. Null-terminated string. - * @return #SPDY_NO on error or #SPDY_YES on success - */ -_MHD_EXTERN int -SPDY_name_value_add (struct SPDY_NameValue *container, - const char *name, - const char *value); - - -/** - * Lookup value for a name in a name/value structure. - * - * @param container structure in which to lookup - * @param name the name to look for - * @param num_values length of the returned array with values - * @return NULL if no such item was found, or an array containing the - * values - */ -_MHD_EXTERN const char * const * -SPDY_name_value_lookup (struct SPDY_NameValue *container, - const char *name, - int *num_values); - - -/** - * Iterate over name/value structure. - * - * @param container structure which to iterate over - * @param iterator callback to call on each name/value pair; - * maybe NULL (then just count headers) - * @param iterator_cls extra argument to @a iterator - * @return number of entries iterated over - */ -_MHD_EXTERN int -SPDY_name_value_iterate (struct SPDY_NameValue *container, - SPDY_NameValueIterator iterator, - void *iterator_cls); - - -/** - * Destroy a NameValue structure. Use this function to destroy only - * objects which, after passed to, will not be destroied by other - * functions. - * - */ -_MHD_EXTERN void -SPDY_name_value_destroy (struct SPDY_NameValue *container); - - -/* SPDY request handling functions */ - - -/** - * Gets the session responsible for the given - * request. - * - * @param request for which the session is wanted - * @return session handler for the request - */ -_MHD_EXTERN struct SPDY_Session * -SPDY_get_session_for_request (const struct SPDY_Request *request); - - -/** - * Associate a void pointer with a request. The data accessible by the - * pointer can later be used wherever the request handler is available. - * - * @param request with which to associate a pointer - * @param cls any data pointed by a pointer to be accessible later - */ -_MHD_EXTERN void -SPDY_set_cls_to_request (struct SPDY_Request *request, - void *cls); - - -/** - * Retrieves the pointer associated with the request by - * SPDY_set_cls_to_request(). - * - * @param request to get its cls - * @return same pointer added by SPDY_set_cls_to_request() or - * NULL when nothing was associated - */ -_MHD_EXTERN void * -SPDY_get_cls_from_request (struct SPDY_Request *request); - - -/* SPDY response handling functions */ - - -/** - * Create response object containing all needed headers and data. The - * response object is not bound to a request, so it can be used multiple - * times with SPDY_queue_response() and schould be - * destroied by calling the SPDY_destroy_response().<p> - * - * Currently the library does not provide compression of the body data. - * It is up to the user to pass already compressed data and the - * appropriate headers to this function when desired. - * - * @param status HTTP status code for the response (e.g. 404) - * @param statustext HTTP status message for the response, which will - * be appended to the status code (e.g. "OK"). Can be NULL - * @param version HTTP version for the response (e.g. "http/1.1") - * @param headers name/value structure containing additional HTTP headers. - * Can be NULL. Can be used multiple times, it is up to - * the user to destoy the object when not needed anymore. - * @param data the body of the response. The lib will make a copy of it, - * so it is up to the user to take care of the memory - * pointed by data - * @param size length of @a data. It can be 0, then the lib will send only - * headers - * @return NULL on error, handle to response object on success - */ -_MHD_EXTERN struct SPDY_Response * -SPDY_build_response (int status, - const char *statustext, - const char *version, - struct SPDY_NameValue *headers, - const void *data, - size_t size); - - -/** - * Create response object containing all needed headers. The data will - * be provided later when the lib calls the callback function (just - * before writing it to the TLS socket). The - * response object is not bound to a request, so it can be used multiple - * times with SPDY_queue_response() and schould be - * destroied by calling the SPDY_destroy_response().<p> - * - * Currently the library does not provide compression of the body data. - * It is up to the user to pass already compressed data and the - * appropriate headers to this function and the callback when desired. - * - * @param status HTTP status code for the response (e.g. 404) - * @param statustext HTTP status message for the response, which will - * be appended to the status code (e.g. "OK"). Can be NULL - * @param version HTTP version for the response (e.g. "http/1.1") - * @param headers name/value structure containing additional HTTP headers. - * Can be NULL. Can be used multiple times, it is up to - * the user to destoy the object when not needed anymore. - * @param rcb callback to use to obtain response data - * @param rcb_cls extra argument to @a rcb - * @param block_size preferred block size for querying rcb (advisory only, - * the lib will call rcb specifying the block size); clients - * should pick a value that is appropriate for IO and - * memory performance requirements. The function will - * fail if the value is bigger than the maximum - * supported value (SPDY_MAX_SUPPORTED_FRAME_SIZE). - * Can be 0, then the lib will use - * #SPDY_MAX_SUPPORTED_FRAME_SIZE instead. - * @return NULL on error, handle to response object on success - */ -_MHD_EXTERN struct SPDY_Response * -SPDY_build_response_with_callback(int status, - const char *statustext, - const char *version, - struct SPDY_NameValue *headers, - SPDY_ResponseCallback rcb, - void *rcb_cls, - uint32_t block_size); - - -/** - * Queue response object to be sent to the client. A successfully queued - * response may never be sent, e.g. when the stream gets closed. The - * data will be added to the output queue. The call will fail, if the - * output for this session - * is closed (i.e. the session is closed, half or full) or the output - * channel for the stream, on which the request was received, is closed - * (i.e. the stream is closed, half or full). - * - * @param request object identifying the request to which the - * response is returned - * @param response object containg headers and data to be sent - * @param closestream TRUE if the server does NOT intend to PUSH - * something more associated to this request/response later, - * FALSE otherwise - * @param consider_priority if FALSE, the response will be added to the - * end of the queue. If TRUE, the response will be added after - * the last previously added response with priority of the - * request grater or equal to that of the current one. This - * means that the function should be called with TRUE each time - * if one wants to be sure that the output queue behaves like - * a priority queue - * @param rrcb callback called when all the data was sent (last frame - * from response) or when that frame was discarded (e.g. the - * stream has been closed meanwhile) - * @param rrcb_cls extra argument to @a rrcb - * @return #SPDY_NO on error or #SPDY_YES on success - */ -_MHD_EXTERN int -SPDY_queue_response (struct SPDY_Request *request, - struct SPDY_Response *response, - bool closestream, - bool consider_priority, - SPDY_ResponseResultCallback rrcb, - void *rrcb_cls); - - -/** - * Destroy a response structure. It should be called for all objects - * returned by SPDY_build_response*() functions to free the memory - * associated with the prepared response. It is safe to call this - * function not before being sure that the response will not be used by - * the lib anymore, this means after SPDY_ResponseResultCallback - * callbacks were called for all calls to SPDY_queue_response() passing - * this response. - * - * @param response to destroy - */ -_MHD_EXTERN void -SPDY_destroy_response (struct SPDY_Response *response); - - -/* SPDY settings ID/value data structure handling functions */ - - -/** - * Create a new SettingsIDValue structure. It is needed for putting - * inside tuples of SPDY option, flags and value for sending to the - * client. - * - * @return hendler to the new empty structure or NULL on error - */ -_MHD_EXTERN const struct SPDY_Settings * -SPDY_settings_create (void); - - -/** - * Add or update a tuple to a SettingsIDValue structure. - * - * @param container structure to which the new tuple is added - * @param id SPDY settings ID that will be sent. If this ID already in - * container, the tupple for it will be updated (value and/or - * flags). If it is not in the container, a new tupple will be - * added. - * @param flags SPDY settings flags applied only to this setting - * @param value of the setting - * @return #SPDY_NO on error - * or #SPDY_YES if a new setting was added - */ -_MHD_EXTERN int -SPDY_settings_add (struct SPDY_Settings *container, - enum SPDY_SETTINGS id, - enum SPDY_FLAG_SETTINGS flags, - int32_t value); - - -/** - * Lookup value and flags for an ID in a settings ID/value structure. - * - * @param container structure in which to lookup - * @param id SPDY settings ID to search for - * @param flags out param for SPDY settings flags for this setting; - * check it against the flags in enum SPDY_FLAG_SETTINGS - * @param value out param for the value of this setting - * @return #SPDY_NO if the setting is not into the structure - * or #SPDY_YES if it is into it - */ -_MHD_EXTERN int -SPDY_settings_lookup (const struct SPDY_Settings *container, - enum SPDY_SETTINGS id, - enum SPDY_FLAG_SETTINGS *flags, - int32_t *value); - - -/** - * Iterate over settings ID/value structure. - * - * @param container structure which to iterate over - * @param iterator callback to call on each ID/value pair; - * maybe NULL (then just count number of settings) - * @param iterator_cls extra argument to iterator - * @return number of entries iterated over - */ -_MHD_EXTERN int -SPDY_settings_iterate (const struct SPDY_Settings *container, - SPDY_SettingsIterator iterator, - void *iterator_cls); - - -/** - * Destroy a settings ID/value structure. Use this function to destroy - * only objects which, after passed to, will not be destroied by other - * functions. - * - * @param container structure which to detroy - */ -_MHD_EXTERN void -SPDY_settings_destroy (struct SPDY_Settings * container); - - -/* SPDY SETTINGS handling functions */ - - -/** - * Send SPDY SETTINGS to the client. The call will return fail if there - * in invald setting into the settings container (e.g. invalid setting - * ID). - * - * @param session SPDY_Session handler for which settings are being sent - * @param settings ID/value pairs of the settings to be sent. - * Can be used multiple times, it is up to the user to destoy - * the object when not needed anymore. - * @param flags for the whole settings frame. They are valid for all tuples - * @param ... list of options (type-value pairs, - * terminated with #SPDY_SETTINGS_OPTION_END). - * @return SPDY_NO on error or SPDY_YES on - * success - */ -_MHD_EXTERN int -SPDY_send_settings (struct SPDY_Session *session, - struct SPDY_Settings *settings, - enum SPDY_FLAG_SETTINGS_FRAME flags, - ...); - - -/* SPDY misc functions */ - - -/** - * Destroy a request structure. It should be called for all objects - * received as a parameter in SPDY_NewRequestCallback to free the memory - * associated with the request. It is safe to call this - * function not before being sure that the request will not be used by - * the lib anymore, this means after the stream, on which this request - * had been sent, was closed and all SPDY_ResponseResultCallback - * callbacks were called for all calls to SPDY_queue_response() passing - * this request object. - * - * @param request to destroy - */ -_MHD_EXTERN void -SPDY_destroy_request (struct SPDY_Request * request); - - -/** - * Send SPDY ping to the client - * - * @param session handler for which the ping request is sent - * @param rttcb callback called when ping response to the request is - * received - * @param rttcb_cls extra argument to @a rttcb - * @return #SPDY_NO on error or #SPDY_YES on success - */ -_MHD_EXTERN int -SPDY_send_ping (struct SPDY_Session *session, - SPDY_PingCallback rttcb, - void *rttcb_cls); - -#endif diff --git a/src/microspdy/Makefile.am b/src/microspdy/Makefile.am @@ -1,40 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = \ - -I$(top_srcdir)/src/include \ - -I$(top_srcdir)/src/microspdy - -AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS) - - -lib_LTLIBRARIES = \ - libmicrospdy.la - -libmicrospdy_la_SOURCES = \ - io.h io.c \ - io_openssl.h io_openssl.c \ - io_raw.h io_raw.c \ - structures.h structures.c \ - internal.h internal.c \ - daemon.h daemon.c \ - stream.h stream.c \ - compression.h compression.c \ - session.h session.c \ - applicationlayer.c applicationlayer.h \ - alstructures.c alstructures.h -libmicrospdy_la_LIBADD = \ - $(SPDY_LIBDEPS) - -libmicrospdy_la_LDFLAGS = \ - $(SPDY_LIB_LDFLAGS) - -libmicrospdy_la_CPPFLAGS = \ - $(AM_CPPFLAGS) $(SPDY_LIB_CPPFLAGS) \ - -DBUILDING_MHD_LIB=1 - -libmicrospdy_la_CFLAGS = -Wextra \ - $(AM_CFLAGS) $(SPDY_LIB_CFLAGS) - - -if USE_COVERAGE - AM_CFLAGS += --coverage -endif diff --git a/src/microspdy/alstructures.c b/src/microspdy/alstructures.c @@ -1,41 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file alstructures.c - * @brief structures only for the application layer - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "alstructures.h" -#include "internal.h" - -void -SPDY_destroy_request (struct SPDY_Request *request) -{ - if(NULL == request) - { - SPDYF_DEBUG("request is NULL"); - return; - } - //strings into request struct are just references to strings in - //headers, so no need to free them twice - SPDY_name_value_destroy(request->headers); - free(request); -} diff --git a/src/microspdy/alstructures.h b/src/microspdy/alstructures.h @@ -1,79 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file alstructures.h - * @brief structures only for the application layer - * @author Andrey Uzunov - */ - -#ifndef ALSTRUCTURES_H -#define ALSTRUCTURES_H - -#include "platform.h" - - -/** - * Represents a SPDY request. - */ -struct SPDY_Request -{ - /** - * SPDY stream in whose context the request was received - */ - struct SPDYF_Stream *stream; - - /** - * Other HTTP headers from the request - */ - struct SPDY_NameValue *headers; - - /** - * HTTP method - */ - char *method; - - /** - * HTTP path - */ - char *path; - - /** - * HTTP version just like in HTTP request/response: - * "HTTP/1.0" or "HTTP/1.1" currently - */ - char *version; - - /** - * called host as in HTTP - */ - char *host; - - /** - * The scheme used ("http" or "https") - */ - char *scheme; - - /** - * Extra field to be used by the user with set/get func for whatever - * purpose he wants. - */ - void *user_cls; -}; - -#endif diff --git a/src/microspdy/applicationlayer.c b/src/microspdy/applicationlayer.c @@ -1,748 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file applicationlayer.c - * @brief SPDY application or HTTP layer - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "applicationlayer.h" -#include "alstructures.h" -#include "structures.h" -#include "internal.h" -#include "daemon.h" -#include "session.h" - - -void -spdy_callback_response_done(void *cls, - struct SPDY_Response *response, - struct SPDY_Request *request, - enum SPDY_RESPONSE_RESULT status, - bool streamopened) -{ - (void)cls; - (void)status; - (void)streamopened; - - SPDY_destroy_request(request); - SPDY_destroy_response(response); -} - - -/** - * Callback called when new stream is created. It extracts the info from - * the stream to create (HTTP) request object and pass it to the client. - * - * @param cls - * @param stream the new SPDY stream - * @return SPDY_YES on success, SPDY_NO on memomry error - */ -static int -spdy_handler_new_stream (void *cls, - struct SPDYF_Stream * stream) -{ - (void)cls; - unsigned int i; - char *method = NULL; - char *path = NULL; - char *version = NULL; - char *host = NULL; - char *scheme = NULL; - struct SPDY_Request * request = NULL; - struct SPDY_NameValue * headers = NULL; - struct SPDY_NameValue * iterator = stream->headers; - struct SPDY_Daemon *daemon; - - daemon = stream->session->daemon; - - //if the user doesn't care, ignore it - if(NULL == daemon->new_request_cb) - return SPDY_YES; - - if(NULL == (headers=SPDY_name_value_create())) - goto free_and_fail; - - if(NULL==(request = malloc(sizeof(struct SPDY_Request)))) - goto free_and_fail; - - memset(request, 0, sizeof(struct SPDY_Request)); - request->stream = stream; - - /* extract the mandatory fields from stream->headers' structure - * to pass them to the client */ - while(iterator != NULL) - { - if(strcmp(":method",iterator->name) == 0) - { - if(1 != iterator->num_values) - break; - method = iterator->value[0]; - } - else if(strcmp(":path",iterator->name) == 0) - { - if(1 != iterator->num_values) - break; - path = iterator->value[0]; - } - else if(strcmp(":version",iterator->name) == 0) - { - if(1 != iterator->num_values) - break; - version = iterator->value[0]; - } - else if(strcmp(":host",iterator->name) == 0) - { - //TODO can it have more values? - if(1 != iterator->num_values) - break; - host = iterator->value[0]; - } - else if(strcmp(":scheme",iterator->name) == 0) - { - if(1 != iterator->num_values) - break; - scheme = iterator->value[0]; - } - else - for(i=0; i<iterator->num_values; ++i) - if (SPDY_YES != SPDY_name_value_add(headers,iterator->name,iterator->value[i])) - { - SPDY_destroy_request(request); - goto free_and_fail; - } - - iterator = iterator->next; - } - - request->method=method; - request->path=path; - request->version=version; - request->host=host; - request->scheme=scheme; - request->headers=headers; - - //check request validity, all these fields are mandatory for a request - if(NULL == method || strlen(method) == 0 - || NULL == path || strlen(path) == 0 - || NULL == version || strlen(version) == 0 - || NULL == host || strlen(host) == 0 - || NULL == scheme || strlen(scheme) == 0 - ) - { - //TODO HTTP 400 Bad Request must be answered - - SPDYF_DEBUG("Bad request"); - - SPDY_destroy_request(request); - - return SPDY_YES; - } - - //call client's callback function to notify - daemon->new_request_cb(daemon->cls, - request, - stream->priority, - method, - path, - version, - host, - scheme, - headers, - !stream->is_in_closed); - - stream->cls = request; - - return SPDY_YES; - - //for GOTO - free_and_fail: - - SPDY_name_value_destroy(headers); - return SPDY_NO; -} - - -/** - * TODO - */ -static int -spdy_handler_new_data (void * cls, - struct SPDYF_Stream *stream, - const void * buf, - size_t size, - bool more) -{ - return stream->session->daemon->received_data_cb(cls, stream->cls, buf, size, more); -} - - - -/** - * Callback to be called when the response queue object was handled and - * the data was already sent or discarded. - * - * @param cls - * @param response_queue the object which is being handled - * @param status shows if actually the response was sent or it was - * discarded by the lib for any reason (e.g., closing session, - * closing stream, stopping daemon, etc.). It is possible that - * status indicates an error but parts of the response headers - * and/or body (in one - * or several frames) were already sent to the client. - */ -static void -spdy_handler_response_queue_result(void * cls, - struct SPDYF_Response_Queue *response_queue, - enum SPDY_RESPONSE_RESULT status) -{ - int streamopened; - struct SPDY_Request *request = (struct SPDY_Request *)cls; - - SPDYF_ASSERT( ( (NULL == response_queue->data_frame) && - (NULL != response_queue->control_frame) ) || - ( (NULL != response_queue->data_frame) && - (NULL == response_queue->control_frame) ), - "response queue must have either control frame or data frame"); - - streamopened = !response_queue->stream->is_out_closed; - - response_queue->rrcb(response_queue->rrcb_cls, response_queue->response, request, status, streamopened); -} - - -int -(SPDY_init) (enum SPDY_IO_SUBSYSTEM io_subsystem, ...) -{ - SPDYF_ASSERT(SPDYF_BUFFER_SIZE >= SPDY_MAX_SUPPORTED_FRAME_SIZE, - "Buffer size is less than max supported frame size!"); - SPDYF_ASSERT(SPDY_MAX_SUPPORTED_FRAME_SIZE >= 32, - "Max supported frame size must be bigger than the minimal value!"); - SPDYF_ASSERT(SPDY_IO_SUBSYSTEM_NONE == spdyf_io_initialized, - "SPDY_init must be called only once per program or after SPDY_deinit"); - - if(SPDY_IO_SUBSYSTEM_OPENSSL & io_subsystem) - { - SPDYF_openssl_global_init(); - spdyf_io_initialized |= SPDY_IO_SUBSYSTEM_OPENSSL; - } - else if(SPDY_IO_SUBSYSTEM_RAW & io_subsystem) - { - SPDYF_raw_global_init(); - spdyf_io_initialized |= SPDY_IO_SUBSYSTEM_RAW; - } - - SPDYF_ASSERT(SPDY_IO_SUBSYSTEM_NONE != spdyf_io_initialized, - "SPDY_init could not find even one IO subsystem"); - - return SPDY_YES; -} - - -void -SPDY_deinit () -{ - SPDYF_ASSERT(SPDY_IO_SUBSYSTEM_NONE != spdyf_io_initialized, - "SPDY_init has not been called!"); - - if(SPDY_IO_SUBSYSTEM_OPENSSL & spdyf_io_initialized) - SPDYF_openssl_global_deinit(); - else if(SPDY_IO_SUBSYSTEM_RAW & spdyf_io_initialized) - SPDYF_raw_global_deinit(); - - spdyf_io_initialized = SPDY_IO_SUBSYSTEM_NONE; -} - - -void -SPDY_run (struct SPDY_Daemon *daemon) -{ - if(NULL == daemon) - { - SPDYF_DEBUG("daemon is NULL"); - return; - } - - SPDYF_run(daemon); -} - - -int -SPDY_get_timeout (struct SPDY_Daemon *daemon, - unsigned long long *timeout) -{ - if(NULL == daemon) - { - SPDYF_DEBUG("daemon is NULL"); - return SPDY_INPUT_ERROR; - } - - return SPDYF_get_timeout(daemon,timeout); -} - - -int -SPDY_get_fdset (struct SPDY_Daemon *daemon, - fd_set *read_fd_set, - fd_set *write_fd_set, - fd_set *except_fd_set) -{ - if(NULL == daemon - || NULL == read_fd_set - || NULL == write_fd_set - || NULL == except_fd_set) - { - SPDYF_DEBUG("a parameter is NULL"); - return SPDY_INPUT_ERROR; - } - - return SPDYF_get_fdset(daemon, - read_fd_set, - write_fd_set, - except_fd_set, - false); -} - - -struct SPDY_Daemon * -SPDY_start_daemon (uint16_t port, - const char *certfile, - const char *keyfile, - SPDY_NewSessionCallback nscb, - SPDY_SessionClosedCallback sccb, - SPDY_NewRequestCallback nrcb, - SPDY_NewDataCallback npdcb, - void * cls, - ...) -{ - struct SPDY_Daemon *daemon; - va_list valist; - - if(SPDY_IO_SUBSYSTEM_NONE == spdyf_io_initialized) - { - SPDYF_DEBUG("library not initialized"); - return NULL; - } - /* - * for now make this checks in framing layer - if(NULL == certfile) - { - SPDYF_DEBUG("certfile is NULL"); - return NULL; - } - if(NULL == keyfile) - { - SPDYF_DEBUG("keyfile is NULL"); - return NULL; - } - */ - - va_start(valist, cls); - daemon = SPDYF_start_daemon_va ( port, - certfile, - keyfile, - nscb, - sccb, - nrcb, - npdcb, - &spdy_handler_new_stream, - &spdy_handler_new_data, - cls, - NULL, - valist - ); - va_end(valist); - - return daemon; -} - - -void -SPDY_stop_daemon (struct SPDY_Daemon *daemon) -{ - if(NULL == daemon) - { - SPDYF_DEBUG("daemon is NULL"); - return; - } - - SPDYF_stop_daemon(daemon); -} - - -struct SPDY_Response * -SPDY_build_response(int status, - const char * statustext, - const char * version, - struct SPDY_NameValue * headers, - const void * data, - size_t size) -{ - struct SPDY_Response *response = NULL; - struct SPDY_NameValue ** all_headers = NULL; //TODO maybe array in stack is enough - char *fullstatus = NULL; - int ret; - int num_hdr_containers = 1; - - if(NULL == version) - { - SPDYF_DEBUG("version is NULL"); - return NULL; - } - - if(NULL == (response = malloc(sizeof(struct SPDY_Response)))) - goto free_and_fail; - memset(response, 0, sizeof(struct SPDY_Response)); - - if(NULL != headers && !SPDYF_name_value_is_empty(headers)) - num_hdr_containers = 2; - - if(NULL == (all_headers = malloc(num_hdr_containers * sizeof(struct SPDY_NameValue *)))) - goto free_and_fail; - memset(all_headers, 0, num_hdr_containers * sizeof(struct SPDY_NameValue *)); - - if(2 == num_hdr_containers) - all_headers[1] = headers; - - if(NULL == (all_headers[0] = SPDY_name_value_create())) - goto free_and_fail; - - if(NULL == statustext) - ret = asprintf(&fullstatus, "%i", status); - else - ret = asprintf(&fullstatus, "%i %s", status, statustext); - if(-1 == ret) - goto free_and_fail; - - if(SPDY_YES != SPDY_name_value_add(all_headers[0], ":status", fullstatus)) - goto free_and_fail; - - free(fullstatus); - fullstatus = NULL; - - if(SPDY_YES != SPDY_name_value_add(all_headers[0], ":version", version)) - goto free_and_fail; - - if(0 >= (response->headers_size = SPDYF_name_value_to_stream(all_headers, - num_hdr_containers, - &(response->headers)))) - goto free_and_fail; - - SPDY_name_value_destroy(all_headers[0]); - free(all_headers); - all_headers = NULL; - - if(size > 0) - { - //copy the data to the response object - if(NULL == (response->data = malloc(size))) - { - free(response->headers); - goto free_and_fail; - } - memcpy(response->data, data, size); - response->data_size = size; - } - - return response; - - //for GOTO - free_and_fail: - - free(fullstatus); - if(NULL != all_headers) - SPDY_name_value_destroy(all_headers[0]); - free(all_headers); - free(response); - - return NULL; -} - - -struct SPDY_Response * -SPDY_build_response_with_callback(int status, - const char * statustext, - const char * version, - struct SPDY_NameValue * headers, - SPDY_ResponseCallback rcb, - void *rcb_cls, - uint32_t block_size) -{ - struct SPDY_Response *response; - - if(NULL == rcb) - { - SPDYF_DEBUG("rcb is NULL"); - return NULL; - } - if(block_size > SPDY_MAX_SUPPORTED_FRAME_SIZE) - { - SPDYF_DEBUG("block_size is wrong"); - return NULL; - } - - if(0 == block_size) - block_size = SPDY_MAX_SUPPORTED_FRAME_SIZE; - - response = SPDY_build_response(status, - statustext, - version, - headers, - NULL, - 0); - - if(NULL == response) - { - return NULL; - } - - response->rcb = rcb; - response->rcb_cls = rcb_cls; - response->rcb_block_size = block_size; - - return response; -} - - -int -SPDY_queue_response (struct SPDY_Request * request, - struct SPDY_Response *response, - bool closestream, - bool consider_priority, - SPDY_ResponseResultCallback rrcb, - void * rrcb_cls) -{ - struct SPDYF_Response_Queue *headers_to_queue; - struct SPDYF_Response_Queue *body_to_queue; - SPDYF_ResponseQueueResultCallback frqcb = NULL; - void *frqcb_cls = NULL; - int int_consider_priority = consider_priority ? SPDY_YES : SPDY_NO; - - if(NULL == request) - { - SPDYF_DEBUG("request is NULL"); - return SPDY_INPUT_ERROR; - } - if(NULL == response) - { - SPDYF_DEBUG("request is NULL"); - return SPDY_INPUT_ERROR; - } - - if(request->stream->is_out_closed - || SPDY_SESSION_STATUS_CLOSING == request->stream->session->status) - return SPDY_NO; - - if(NULL != rrcb) - { - frqcb_cls = request; - frqcb = &spdy_handler_response_queue_result; - } - - if(response->data_size > 0) - { - //SYN_REPLY and DATA will be queued - - if(NULL == (headers_to_queue = SPDYF_response_queue_create(false, - response->headers, - response->headers_size, - response, - request->stream, - false, - NULL, - NULL, - NULL, - NULL))) - { - return SPDY_NO; - } - - if(NULL == (body_to_queue = SPDYF_response_queue_create(true, - response->data, - response->data_size, - response, - request->stream, - closestream, - frqcb, - frqcb_cls, - rrcb, - rrcb_cls))) - { - SPDYF_response_queue_destroy(headers_to_queue); - return SPDY_NO; - } - - SPDYF_queue_response (headers_to_queue, - request->stream->session, - int_consider_priority); - - SPDYF_queue_response (body_to_queue, - request->stream->session, - int_consider_priority); - } - else if(NULL == response->rcb) - { - //no "body" will be queued, e.g. HTTP 404 without body - - if(NULL == (headers_to_queue = SPDYF_response_queue_create(false, - response->headers, - response->headers_size, - response, - request->stream, - closestream, - frqcb, - frqcb_cls, - rrcb, - rrcb_cls))) - { - return SPDY_NO; - } - - SPDYF_queue_response (headers_to_queue, - request->stream->session, - int_consider_priority); - } - else - { - //response with callbacks - - if(NULL == (headers_to_queue = SPDYF_response_queue_create(false, - response->headers, - response->headers_size, - response, - request->stream, - false, - NULL, - NULL, - NULL, - NULL))) - { - return SPDY_NO; - } - - if(NULL == (body_to_queue = SPDYF_response_queue_create(true, - response->data, - response->data_size, - response, - request->stream, - closestream, - frqcb, - frqcb_cls, - rrcb, - rrcb_cls))) - { - SPDYF_response_queue_destroy(headers_to_queue); - return SPDY_NO; - } - - SPDYF_queue_response (headers_to_queue, - request->stream->session, - int_consider_priority); - - SPDYF_queue_response (body_to_queue, - request->stream->session, - int_consider_priority); - } - - return SPDY_YES; -} - - -socklen_t -SPDY_get_remote_addr(struct SPDY_Session * session, - struct sockaddr ** addr) -{ - if(NULL == session) - { - SPDYF_DEBUG("session is NULL"); - return 0; - } - - *addr = session->addr; - - return session->addr_len; -} - - -struct SPDY_Session * -SPDY_get_session_for_request(const struct SPDY_Request * request) -{ - if(NULL == request) - { - SPDYF_DEBUG("request is NULL"); - return NULL; - } - - return request->stream->session; -} - - -void * -SPDY_get_cls_from_session(struct SPDY_Session * session) -{ - if(NULL == session) - { - SPDYF_DEBUG("session is NULL"); - return NULL; - } - - return session->user_cls; -} - - -void -SPDY_set_cls_to_session(struct SPDY_Session * session, - void * cls) -{ - if(NULL == session) - { - SPDYF_DEBUG("session is NULL"); - return; - } - - session->user_cls = cls; -} - - -void * -SPDY_get_cls_from_request(struct SPDY_Request * request) -{ - if(NULL == request) - { - SPDYF_DEBUG("request is NULL"); - return NULL; - } - - return request->user_cls; -} - - -void -SPDY_set_cls_to_request(struct SPDY_Request * request, - void * cls) -{ - if(NULL == request) - { - SPDYF_DEBUG("request is NULL"); - return; - } - - request->user_cls = cls; -} diff --git a/src/microspdy/applicationlayer.h b/src/microspdy/applicationlayer.h @@ -1,31 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file applicationlayer.h - * @brief SPDY application or HTTP layer - * @author Andrey Uzunov - */ - -#ifndef APPLICATIONLAYER_H -#define APPLICATIONLAYER_H - -#include "platform.h" - - -#endif diff --git a/src/microspdy/compression.c b/src/microspdy/compression.c @@ -1,441 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file compression.c - * @brief zlib handling functions - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "structures.h" -#include "internal.h" -#include "compression.h" - -/* spdy ver 3 specific dictionary used by zlib */ -static const unsigned char -spdyf_zlib_dictionary[] = { - 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // - - - - o p t i - 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // o n s - - - - h - 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // e a d - - - - p - 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // o s t - - - - p - 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // u t - - - - d e - 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // l e t e - - - - - 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // t r a c e - - - - 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // - a c c e p t - - 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p - 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t - c h a r s e - 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t - - - - a c c - 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e p t - e n c o - 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // d i n g - - - - - 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // a c c e p t - l - 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // a n g u a g e - - 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p - 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t - r a n g e s - 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // - - - - a g e - - 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // - - - a l l o w - 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // - - - - a u t h - 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // o r i z a t i o - 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n - - - - c a c - 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // h e - c o n t r - 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // o l - - - - c o - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // n n e c t i o n - 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t - 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // e n t - b a s e - 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t - 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e n t - e n c o - 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // d i n g - - - - - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // c o n t e n t - - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // l a n g u a g e - 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t - 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // e n t - l e n g - 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // t h - - - - c o - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // n t e n t - l o - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // c a t i o n - - - 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n - 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t - m d 5 - - - - 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // - c o n t e n t - 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // - r a n g e - - - 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n - 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t - t y p e - - - 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // - - d a t e - - - 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // - - e t a g - - - 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // - - e x p e c t - 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // - - - - e x p i - 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // r e s - - - - f - 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // r o m - - - - h - 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // o s t - - - - i - 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f - m a t c h - - 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // - - - i f - m o - 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // d i f i e d - s - 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // i n c e - - - - - 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // i f - n o n e - - 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // m a t c h - - - - 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // - i f - r a n g - 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e - - - - i f - - 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // u n m o d i f i - 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // e d - s i n c e - 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // - - - - l a s t - 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // - m o d i f i e - 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d - - - - l o c - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // a t i o n - - - - 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // - m a x - f o r - 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // w a r d s - - - - 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // - p r a g m a - - 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // - - - p r o x y - 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // - a u t h e n t - 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // i c a t e - - - - 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // - p r o x y - a - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // u t h o r i z a - 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // t i o n - - - - - 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // r a n g e - - - - 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // - r e f e r e r - 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // - - - - r e t r - 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y - a f t e r - - 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // - - - s e r v e - 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r - - - - t e - - 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // - - - t r a i l - 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // e r - - - - t r - 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // a n s f e r - e - 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // n c o d i n g - - 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // - - - u p g r a - 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // d e - - - - u s - 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // e r - a g e n t - 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // - - - - v a r y - 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // - - - - v i a - - 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // - - - w a r n i - 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // n g - - - - w w - 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w - a u t h e n - 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // t i c a t e - - - 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // - - m e t h o d - 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // - - - - g e t - - 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // - - - s t a t u - 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s - - - - 2 0 0 - 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // - O K - - - - v - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // e r s i o n - - - 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // - - H T T P - 1 - 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // - 1 - - - - u r - 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l - - - - p u b - 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // l i c - - - - s - 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // e t - c o o k i - 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e - - - - k e e - 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p - a l i v e - - 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // - - - o r i g i - 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n 1 0 0 1 0 1 2 - 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 0 1 2 0 2 2 0 5 - 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 2 0 6 3 0 0 3 0 - 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 2 3 0 3 3 0 4 3 - 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 0 5 3 0 6 3 0 7 - 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 4 0 2 4 0 5 4 0 - 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 6 4 0 7 4 0 8 4 - 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 0 9 4 1 0 4 1 1 - 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 4 1 2 4 1 3 4 1 - 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 4 4 1 5 4 1 6 4 - 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 1 7 5 0 2 5 0 4 - 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 5 0 5 2 0 3 - N - 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // o n - A u t h o - 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // r i t a t i v e - 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // - I n f o r m a - 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // t i o n 2 0 4 - - 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // N o - C o n t e - 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // n t 3 0 1 - M o - 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // v e d - P e r m - 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // a n e n t l y 4 - 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 0 0 - B a d - R - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // e q u e s t 4 0 - 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1 - U n a u t h - 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // o r i z e d 4 0 - 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3 - F o r b i d - 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // d e n 4 0 4 - N - 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // o t - F o u n d - 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 5 0 0 - I n t e - 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // r n a l - S e r - 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // v e r - E r r o - 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r 5 0 1 - N o t - 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // - I m p l e m e - 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // n t e d 5 0 3 - - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // S e r v i c e - - 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // U n a v a i l a - 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // b l e J a n - F - 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // e b - M a r - A - 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // p r - M a y - J - 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // u n - J u l - A - 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // u g - S e p t - - 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // O c t - N o v - - 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // D e c - 0 0 - 0 - 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0 - 0 0 - M o n - 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // - - T u e - - W - 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // e d - - T h u - - 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // - F r i - - S a - 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t - - S u n - - - 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // G M T c h u n k - 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // e d - t e x t - - 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // h t m l - i m a - 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // g e - p n g - i - 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // m a g e - j p g - 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // - i m a g e - g - 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // i f - a p p l i - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x - 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // m l - a p p l i - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x - 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // h t m l - x m l - 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // - t e x t - p l - 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // a i n - t e x t - 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // - j a v a s c r - 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // i p t - p u b l - 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // i c p r i v a t - 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // e m a x - a g e - 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // - g z i p - d e - 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // f l a t e - s d - 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // c h c h a r s e - 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t - u t f - 8 c - 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // h a r s e t - i - 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // s o - 8 8 5 9 - - 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1 - u t f - - - - 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // - e n q - 0 - -}; - - -int -SPDYF_zlib_deflate_init(z_stream *strm) -{ - int ret; - - strm->zalloc = Z_NULL; - strm->zfree = Z_NULL; - strm->opaque = Z_NULL; - //the second argument is "level of compression" - //use 0 for no compression; 9 for best compression - ret = deflateInit(strm, Z_DEFAULT_COMPRESSION); - if(ret != Z_OK) - { - SPDYF_DEBUG("deflate init"); - return SPDY_NO; - } - ret = deflateSetDictionary(strm, - spdyf_zlib_dictionary, - sizeof(spdyf_zlib_dictionary)); - if(ret != Z_OK) - { - SPDYF_DEBUG("deflate set dict"); - deflateEnd(strm); - return SPDY_NO; - } - return SPDY_YES; -} - - -void -SPDYF_zlib_deflate_end(z_stream *strm) -{ - deflateEnd(strm); -} - -int -SPDYF_zlib_deflate(z_stream *strm, - const void *src, - size_t src_size, - size_t *data_used, - void **dest, - size_t *dest_size) -{ - int ret; - int flush; - unsigned int have; - Bytef out[SPDYF_ZLIB_CHUNK]; - - *dest = NULL; - *dest_size = 0; - - do - { - /* check for big data bigger than the buffer used */ - if(src_size > SPDYF_ZLIB_CHUNK) - { - strm->avail_in = SPDYF_ZLIB_CHUNK; - src_size -= SPDYF_ZLIB_CHUNK; - /* flush is used for the loop to detect if we still - * need to supply additional - * data to the stream via avail_in and next_in. */ - flush = Z_NO_FLUSH; - } - else - { - strm->avail_in = src_size; - flush = Z_SYNC_FLUSH; - } - *data_used += strm->avail_in; - - strm->next_in = (Bytef *)src; - - /* Loop while output data is available */ - do - { - strm->avail_out = SPDYF_ZLIB_CHUNK; - strm->next_out = out; - - /* No need to check return value of deflate. - * (See zlib documentation at http://www.zlib.net/zlib_how.html */ - ret = deflate(strm, flush); - have = SPDYF_ZLIB_CHUNK - strm->avail_out; - - /* (Re)allocate memory for dest and keep track of it's size. */ - *dest_size += have; - *dest = realloc(*dest, *dest_size); - if(!*dest) - { - SPDYF_DEBUG("realloc data for result"); - deflateEnd(strm); - return SPDY_NO; - } - memcpy((*dest) + ((*dest_size) - have), out, have); - } - while(strm->avail_out == 0); - /* At this point, all of the input data should already - * have been used. */ - SPDYF_ASSERT(strm->avail_in == 0,"compressing bug"); - } - while(flush != Z_SYNC_FLUSH); - - return Z_OK == ret ? SPDY_YES : SPDY_NO; -} - - -int -SPDYF_zlib_inflate_init(z_stream *strm) -{ - int ret; - - strm->zalloc = Z_NULL; - strm->zfree = Z_NULL; - strm->opaque = Z_NULL; - strm->avail_in = 0; - strm->next_in = Z_NULL; - //change 15 to lower value for performance and benchmark - //"The windowBits parameter is the base two logarithm of the - // maximum window size (the size of the history buffer)." - ret = inflateInit2(strm, 15); - if(ret != Z_OK) - { - SPDYF_DEBUG("Cannot inflateInit2 the stream"); - return SPDY_NO; - } - return SPDY_YES; -} - - -void -SPDYF_zlib_inflate_end(z_stream *strm) -{ - inflateEnd(strm); -} - - -int -SPDYF_zlib_inflate(z_stream *strm, - const void *src, - size_t src_size, - void **dest, - size_t *dest_size) -{ - int ret = Z_OK; - uint32_t have; - Bytef out[SPDYF_ZLIB_CHUNK]; - - *dest = NULL; - *dest_size = 0; - - /* decompress until deflate stream ends or end of file */ - do - { - if(src_size > SPDYF_ZLIB_CHUNK) - { - strm->avail_in = SPDYF_ZLIB_CHUNK; - src_size -= SPDYF_ZLIB_CHUNK; - } - else - { - strm->avail_in = src_size; - src_size = 0; - } - - if(strm->avail_in == 0){ - //the loop breaks always here as the stream never ends - break; - } - - strm->next_in = (Bytef *) src; - /* run inflate() on input until output buffer not full */ - do { - strm->avail_out = SPDYF_ZLIB_CHUNK; - strm->next_out = out; - ret = inflate(strm, Z_SYNC_FLUSH); - - switch (ret) - { - case Z_STREAM_ERROR: - SPDYF_DEBUG("Error on inflate"); - //no inflateEnd here, same in zlib example - return SPDY_NO; - - case Z_NEED_DICT: - ret = inflateSetDictionary(strm, - spdyf_zlib_dictionary, - sizeof(spdyf_zlib_dictionary)); - if(ret != Z_OK) - { - SPDYF_DEBUG("Error on inflateSetDictionary"); - inflateEnd(strm); - return SPDY_NO; - } - ret = inflate(strm, Z_SYNC_FLUSH); - if(Z_STREAM_ERROR == ret) - { - SPDYF_DEBUG("Error on inflate"); - return SPDY_NO; - } - break; - - case Z_DATA_ERROR: - SPDYF_DEBUG("Z_DATA_ERROR"); - inflateEnd(strm); - return SPDY_NO; - - case Z_MEM_ERROR: - SPDYF_DEBUG("Z_MEM_ERROR"); - inflateEnd(strm); - return SPDY_NO; - } - have = SPDYF_ZLIB_CHUNK - strm->avail_out; - *dest_size += have; - /* (re)alloc memory for the output buffer */ - *dest = realloc(*dest, *dest_size); - if(!*dest) - { - SPDYF_DEBUG("Cannot realloc memory"); - inflateEnd(strm); - return SPDY_NO; - } - memcpy((*dest) + ((*dest_size) - have), out, have); - } - while (0 == strm->avail_out); - } - while (Z_STREAM_END != ret); - - return Z_OK == ret || Z_STREAM_END == ret ? SPDY_YES : SPDY_NO; -} diff --git a/src/microspdy/compression.h b/src/microspdy/compression.h @@ -1,117 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file compression.h - * @brief zlib handling functions - * @author Andrey Uzunov - */ - -#ifndef COMPRESSION_H -#define COMPRESSION_H - -#include "platform.h" - -/* size of buffers used by zlib on (de)compressing */ -#define SPDYF_ZLIB_CHUNK 16384 - - -/** - * Initializes the zlib stream for compression. Must be called once - * for a session on initialization. - * - * @param strm Zlib stream on which we work - * @return SPDY_NO if zlib failed. SPDY_YES otherwise - */ -int -SPDYF_zlib_deflate_init(z_stream *strm); - - -/** - * Deinitializes the zlib stream for compression. Should be called once - * for a session on cleaning up. - * - * @param strm Zlib stream on which we work - */ -void -SPDYF_zlib_deflate_end(z_stream *strm); - - -/** - * Compressing stream with zlib. - * - * @param strm Zlib stream on which we work - * @param src stream of the data to be compressed - * @param src_size size of the data - * @param data_used the number of bytes from src_stream that were used - * TODO do we need - * @param dest the resulting compressed stream. Should be NULL. Must be - * freed later manually. - * @param dest_size size of the data after compression - * @return SPDY_NO if malloc or zlib failed. SPDY_YES otherwise - */ -int -SPDYF_zlib_deflate(z_stream *strm, - const void *src, - size_t src_size, - size_t *data_used, - void **dest, - size_t *dest_size); - - -/** - * Initializes the zlib stream for decompression. Must be called once - * for a session. - * - * @param strm Zlib stream on which we work - * @return SPDY_NO if zlib failed. SPDY_YES otherwise - */ -int -SPDYF_zlib_inflate_init(z_stream *strm); - - -/** - * Deinitializes the zlib stream for decompression. Should be called once - * for a session on cleaning up. - * - * @param strm Zlib stream on which we work - */ -void -SPDYF_zlib_inflate_end(z_stream *strm); - - -/** - * Decompressing stream with zlib. - * - * @param strm Zlib stream on which we work - * @param src stream of the data to be decompressed - * @param src_size size of the data - * @param dest the resulting decompressed stream. Should be NULL. Must - * be freed manually. - * @param dest_size size of the data after decompression - * @return SPDY_NO if malloc or zlib failed. SPDY_YES otherwise. If the - * function fails, the SPDY session must be closed - */ -int -SPDYF_zlib_inflate(z_stream *strm, - const void *src, - size_t src_size, - void **dest, - size_t *dest_size); - -#endif diff --git a/src/microspdy/daemon.c b/src/microspdy/daemon.c @@ -1,544 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file microspdy/daemon.c - * @brief daemon functionality - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "structures.h" -#include "internal.h" -#include "session.h" -#include "io.h" - - -/** - * Default implementation of the panic function, - * prints an error message and aborts. - * - * @param cls unused - * @param file name of the file with the problem - * @param line line number with the problem - * @param reason error message with details - */ -static void -spdyf_panic_std (void *cls, - const char *file, - unsigned int line, - const char *reason) -{ - (void)cls; - fprintf (stdout, "Fatal error in libmicrospdy %s:%u: %s\n", - file, line, reason); - //raise(SIGINT); //used for gdb - abort (); -} - - -/** - * Global handler for fatal errors. - */ -SPDY_PanicCallback spdyf_panic = &spdyf_panic_std; - - -/** - * Global closure argument for "spdyf_panic". - */ -void *spdyf_panic_cls; - - -/** - * Free resources associated with all closed connections. - * (destroy responses, free buffers, etc.). - * - * @param daemon daemon to clean up - */ -static void -spdyf_cleanup_sessions (struct SPDY_Daemon *daemon) -{ - struct SPDY_Session *session; - - while (NULL != (session = daemon->cleanup_head)) - { - DLL_remove (daemon->cleanup_head, - daemon->cleanup_tail, - session); - - SPDYF_session_destroy(session); - } -} - - -/** - * Closing of all connections handled by the daemon. - * - * @param daemon SPDY daemon - */ -static void -spdyf_close_all_sessions (struct SPDY_Daemon *daemon) -{ - struct SPDY_Session *session; - - while (NULL != (session = daemon->sessions_head)) - { - //prepare GOAWAY frame - SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_OK, true); - //try to send the frame (it is best effort, so it will maybe sent) - SPDYF_session_write(session,true); - SPDYF_session_close(session); - } - - spdyf_cleanup_sessions(daemon); -} - - -/** - * Parse a list of options given as varargs. - * - * @param daemon the daemon to initialize - * @param valist the options - * @return SPDY_YES on success, SPDY_NO on error - */ -static int -spdyf_parse_options_va (struct SPDY_Daemon *daemon, - va_list valist) -{ - enum SPDY_DAEMON_OPTION opt; - - while (SPDY_DAEMON_OPTION_END != (opt = (enum SPDY_DAEMON_OPTION) va_arg (valist, int))) - { - if(opt & daemon->options) - { - SPDYF_DEBUG("Daemon option %i used twice",opt); - return SPDY_NO; - } - daemon->options |= opt; - - switch (opt) - { - case SPDY_DAEMON_OPTION_SESSION_TIMEOUT: - daemon->session_timeout = va_arg (valist, unsigned int) * 1000; - break; - case SPDY_DAEMON_OPTION_SOCK_ADDR: - daemon->address = va_arg (valist, struct sockaddr *); - break; - case SPDY_DAEMON_OPTION_FLAGS: - daemon->flags = va_arg (valist, enum SPDY_DAEMON_FLAG); - break; - case SPDY_DAEMON_OPTION_IO_SUBSYSTEM: - daemon->io_subsystem = va_arg (valist, enum SPDY_IO_SUBSYSTEM); - break; - case SPDY_DAEMON_OPTION_MAX_NUM_FRAMES: - daemon->max_num_frames = va_arg (valist, uint32_t); - break; - default: - SPDYF_DEBUG("Wrong option for the daemon %i",opt); - return SPDY_NO; - } - } - return SPDY_YES; -} - - -void -SPDY_set_panic_func (SPDY_PanicCallback cb, - void *cls) -{ - spdyf_panic = cb; - spdyf_panic_cls = cls; -} - - -struct SPDY_Daemon * -SPDYF_start_daemon_va (uint16_t port, - const char *certfile, - const char *keyfile, - SPDY_NewSessionCallback nscb, - SPDY_SessionClosedCallback sccb, - SPDY_NewRequestCallback nrcb, - SPDY_NewDataCallback npdcb, - SPDYF_NewStreamCallback fnscb, - SPDYF_NewDataCallback fndcb, - void * cls, - void * fcls, - va_list valist) -{ - struct SPDY_Daemon *daemon = NULL; - int afamily; - int option_on = 1; - int ret; - struct sockaddr_in* servaddr4 = NULL; -#if HAVE_INET6 - struct sockaddr_in6* servaddr6 = NULL; -#endif - socklen_t addrlen; - - if (NULL == (daemon = malloc (sizeof (struct SPDY_Daemon)))) - { - SPDYF_DEBUG("malloc"); - return NULL; - } - memset (daemon, 0, sizeof (struct SPDY_Daemon)); - daemon->socket_fd = -1; - daemon->port = port; - - if(SPDY_YES != spdyf_parse_options_va (daemon, valist)) - { - SPDYF_DEBUG("parse"); - goto free_and_fail; - } - - if(0 == daemon->max_num_frames) - daemon->max_num_frames = SPDYF_NUM_SENT_FRAMES_AT_ONCE; - - if(!port && NULL == daemon->address) - { - SPDYF_DEBUG("Port is 0"); - goto free_and_fail; - } - if(0 == daemon->io_subsystem) - daemon->io_subsystem = SPDY_IO_SUBSYSTEM_OPENSSL; - - if(SPDY_YES != SPDYF_io_set_daemon(daemon, daemon->io_subsystem)) - goto free_and_fail; - - if(SPDY_IO_SUBSYSTEM_RAW != daemon->io_subsystem) - { - if (NULL == certfile - || NULL == (daemon->certfile = strdup (certfile))) - { - SPDYF_DEBUG("strdup (certfile)"); - goto free_and_fail; - } - if (NULL == keyfile - || NULL == (daemon->keyfile = strdup (keyfile))) - { - SPDYF_DEBUG("strdup (keyfile)"); - goto free_and_fail; - } - } - - daemon->new_session_cb = nscb; - daemon->session_closed_cb = sccb; - daemon->new_request_cb = nrcb; - daemon->received_data_cb = npdcb; - daemon->cls = cls; - daemon->fcls = fcls; - daemon->fnew_stream_cb = fnscb; - daemon->freceived_data_cb = fndcb; - -#if HAVE_INET6 - //handling IPv6 - if((daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6) - && NULL != daemon->address && AF_INET6 != daemon->address->sa_family) - { - SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but IPv4 address provided"); - goto free_and_fail; - } - - addrlen = sizeof (struct sockaddr_in6); - - if(NULL == daemon->address) - { - if (NULL == (servaddr6 = malloc (addrlen))) - { - SPDYF_DEBUG("malloc"); - goto free_and_fail; - } - memset (servaddr6, 0, addrlen); - servaddr6->sin6_family = AF_INET6; - servaddr6->sin6_addr = in6addr_any; - servaddr6->sin6_port = htons (port); - daemon->address = (struct sockaddr *) servaddr6; - } - - if(AF_INET6 == daemon->address->sa_family) - { - afamily = PF_INET6; - } - else - { - afamily = PF_INET; - } -#else - //handling IPv4 - if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6) - { - SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but no support"); - goto free_and_fail; - } - - addrlen = sizeof (struct sockaddr_in); - - if(NULL == daemon->address) - { - if (NULL == (servaddr4 = malloc (addrlen))) - { - SPDYF_DEBUG("malloc"); - goto free_and_fail; - } - memset (servaddr4, 0, addrlen); - servaddr4->sin_family = AF_INET; - servaddr4->sin_addr = INADDR_ANY; - servaddr4->sin_port = htons (port); - daemon->address = (struct sockaddr *) servaddr4; - } - - afamily = PF_INET; -#endif - - daemon->socket_fd = socket (afamily, SOCK_STREAM, 0); - if (-1 == daemon->socket_fd) - { - SPDYF_DEBUG("sock"); - goto free_and_fail; - } - - //setting option for the socket to reuse address - ret = setsockopt(daemon->socket_fd, SOL_SOCKET, SO_REUSEADDR, &option_on, sizeof(option_on)); - if(ret) - { - SPDYF_DEBUG("WARNING: SO_REUSEADDR was not set for the server"); - } - -#if HAVE_INET6 - if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6) - { - ret = setsockopt(daemon->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &option_on, sizeof(option_on)); - if(ret) - { - SPDYF_DEBUG("setsockopt with IPPROTO_IPV6 failed"); - goto free_and_fail; - } - } -#endif - - if (-1 == bind (daemon->socket_fd, daemon->address, addrlen)) - { - SPDYF_DEBUG("bind %i",errno); - goto free_and_fail; - } - - if (listen (daemon->socket_fd, 20) < 0) - { - SPDYF_DEBUG("listen %i",errno); - goto free_and_fail; - } - - if(SPDY_YES != daemon->fio_init(daemon)) - { - SPDYF_DEBUG("tls"); - goto free_and_fail; - } - - return daemon; - - //for GOTO - free_and_fail: - if(daemon->socket_fd > 0) - (void)MHD_socket_close_ (daemon->socket_fd); - - free(servaddr4); -#if HAVE_INET6 - free(servaddr6); -#endif - if(NULL != daemon->certfile) - free(daemon->certfile); - if(NULL != daemon->keyfile) - free(daemon->keyfile); - free (daemon); - - return NULL; -} - - -void -SPDYF_stop_daemon (struct SPDY_Daemon *daemon) -{ - daemon->fio_deinit(daemon); - - shutdown (daemon->socket_fd, SHUT_RDWR); - spdyf_close_all_sessions (daemon); - (void)MHD_socket_close_ (daemon->socket_fd); - - if(!(SPDY_DAEMON_OPTION_SOCK_ADDR & daemon->options)) - free(daemon->address); - - free(daemon->certfile); - free(daemon->keyfile); - - free(daemon); -} - - -int -SPDYF_get_timeout (struct SPDY_Daemon *daemon, - unsigned long long *timeout) -{ - unsigned long long earliest_deadline = 0; - unsigned long long now; - struct SPDY_Session *pos; - bool have_timeout; - - if(0 == daemon->session_timeout) - return SPDY_NO; - - now = SPDYF_monotonic_time(); - have_timeout = false; - for (pos = daemon->sessions_head; NULL != pos; pos = pos->next) - { - if ( (! have_timeout) || - (earliest_deadline > pos->last_activity + daemon->session_timeout) ) - earliest_deadline = pos->last_activity + daemon->session_timeout; - - have_timeout = true; - - if (SPDY_YES == pos->fio_is_pending(pos)) - { - earliest_deadline = 0; - break; - } - } - - if (!have_timeout) - return SPDY_NO; - if (earliest_deadline <= now) - *timeout = 0; - else - *timeout = earliest_deadline - now; - - return SPDY_YES; -} - - -int -SPDYF_get_fdset (struct SPDY_Daemon *daemon, - fd_set *read_fd_set, - fd_set *write_fd_set, - fd_set *except_fd_set, - bool all) -{ - (void)except_fd_set; - struct SPDY_Session *pos; - int fd; - int max_fd = -1; - - fd = daemon->socket_fd; - if (-1 != fd) - { - FD_SET (fd, read_fd_set); - /* update max file descriptor */ - max_fd = fd; - } - - for (pos = daemon->sessions_head; NULL != pos; pos = pos->next) - { - fd = pos->socket_fd; - FD_SET(fd, read_fd_set); - if (all - || (NULL != pos->response_queue_head) //frames pending - || (NULL != pos->write_buffer) //part of last frame pending - || (SPDY_SESSION_STATUS_CLOSING == pos->status) //the session is about to be closed - || (daemon->session_timeout //timeout passed for the session - && (pos->last_activity + daemon->session_timeout < SPDYF_monotonic_time())) - || (SPDY_YES == pos->fio_is_pending(pos)) //data in TLS' read buffer pending - || ((pos->read_buffer_offset - pos->read_buffer_beginning) > 0) // data in lib's read buffer pending - ) - FD_SET(fd, write_fd_set); - if(fd > max_fd) - max_fd = fd; - } - - return max_fd; -} - - -void -SPDYF_run (struct SPDY_Daemon *daemon) -{ - struct SPDY_Session *pos; - struct SPDY_Session *next; - int num_ready; - fd_set rs; - fd_set ws; - fd_set es; - int max; - struct timeval timeout; - int ds; - - timeout.tv_sec = 0; - timeout.tv_usec = 0; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - //here we need really all descriptors to see later which are ready - max = SPDYF_get_fdset(daemon,&rs,&ws,&es, true); - - num_ready = select (max + 1, &rs, &ws, &es, &timeout); - - if(num_ready < 1) - return; - - if ( (-1 != (ds = daemon->socket_fd)) && - (FD_ISSET (ds, &rs)) ){ - SPDYF_session_accept(daemon); - } - - next = daemon->sessions_head; - while (NULL != (pos = next)) - { - next = pos->next; - ds = pos->socket_fd; - if (ds != -1) - { - //fill the read buffer - if (FD_ISSET (ds, &rs) || pos->fio_is_pending(pos)){ - SPDYF_session_read(pos); - } - - //do something with the data in read buffer - if(SPDY_NO == SPDYF_session_idle(pos)) - { - //the session was closed, cannot write anymore - //continue; - } - - //write whatever has been put to the response queue - //during read or idle operation, something might be put - //on the response queue, thus call write operation - if (FD_ISSET (ds, &ws)){ - if(SPDY_NO == SPDYF_session_write(pos, false)) - { - //SPDYF_session_close(pos); - //continue; - } - } - - /* the response queue has been flushed for half closed - * connections, so let close them */ - /*if(pos->read_closed) - { - SPDYF_session_close(pos); - }*/ - } - } - - spdyf_cleanup_sessions(daemon); -} diff --git a/src/microspdy/daemon.h b/src/microspdy/daemon.h @@ -1,130 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file daemon.h - * @brief daemon functionality - * @author Andrey Uzunov - */ - -#ifndef DAEMON_H -#define DAEMON_H - -#include "platform.h" - - -/** - * Global flags containing the initialized IO subsystems. - */ -enum SPDY_IO_SUBSYSTEM spdyf_io_initialized; - - -/** - * Start a SPDDY webserver on the given port. - * - * @param port port to bind to - * @param certfile path to the certificate that will be used by server - * @param keyfile path to the keyfile for the certificate - * @param nscb callback called when a new SPDY session is - * established by a client - * @param sccb callback called when a client closes the session - * @param nrcb callback called when a client sends request - * @param npdcb callback called when HTTP POST params are received - * after request - * @param fnscb callback called when new stream is opened by a client - * @param fndcb callback called when new data -- within a data frame -- - * is received by the server - * @param cls extra argument to all of the callbacks without those - * specific only for the framing layer - * @param fcls extra argument to all of the callbacks, specific only for - * the framing layer (those vars starting with 'f'). - * @param valist va_list of options (type-value pairs, - * terminated with SPDY_DAEMON_OPTION_END). - * @return NULL on error, handle to daemon on success - */ -struct SPDY_Daemon * -SPDYF_start_daemon_va (uint16_t port, - const char *certfile, - const char *keyfile, - SPDY_NewSessionCallback nscb, - SPDY_SessionClosedCallback sccb, - SPDY_NewRequestCallback nrcb, - SPDY_NewDataCallback npdcb, - SPDYF_NewStreamCallback fnscb, - SPDYF_NewDataCallback fndcb, - void * cls, - void * fcls, - va_list valist); - - -/** - * Run webserver operations (without blocking unless - * in client callbacks). This method must be called in the client event - * loop. - * - * @param daemon daemon to run - */ -void -SPDYF_run (struct SPDY_Daemon *daemon); - - -/** - * Obtain timeout value for select for this daemon. The returned value - * is how long select - * should at most block, not the timeout value set for connections. - * - * @param daemon daemon to query for timeout - * @param timeout set to the timeout (in milliseconds) - * @return SPDY_YES on success, SPDY_NO if no connections exist that - * would necessiate the use of a timeout right now - */ -int -SPDYF_get_timeout (struct SPDY_Daemon *daemon, - unsigned long long *timeout); - - -/** - * Obtain the select sets for this daemon. The idea of SPDYF_get_fdset - * is to return such descriptors that the select in the application can - * return and SPDY_run can be called only when this is really needed. - * That means not all sockets will be added to write_fd_set. - * - * @param daemon daemon to get sets from - * @param read_fd_set read set - * @param write_fd_set write set - * @param except_fd_set except set - * @param all add all session's descriptors to write_fd_set or not - * @return largest FD added - */ -int -SPDYF_get_fdset (struct SPDY_Daemon *daemon, - fd_set *read_fd_set, - fd_set *write_fd_set, - fd_set *except_fd_set, - bool all); - - -/** - * Shutdown the daemon. - * - * @param daemon daemon to stop - */ -void -SPDYF_stop_daemon (struct SPDY_Daemon *daemon); - -#endif diff --git a/src/microspdy/internal.c b/src/microspdy/internal.c @@ -1,40 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file microspdy/internal.c - * @brief internal functions and macros for the framing layer - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "structures.h" - - -unsigned long long -SPDYF_monotonic_time (void) -{ -#ifdef HAVE_CLOCK_GETTIME -#ifdef CLOCK_MONOTONIC - struct timespec ts; - if (0 == clock_gettime (CLOCK_MONOTONIC, &ts)) - return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; -#endif -#endif - return time (NULL) * 1000; -} diff --git a/src/microspdy/internal.h b/src/microspdy/internal.h @@ -1,199 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file microspdy/internal.h - * @brief internal functions and macros for the framing layer - * @author Andrey Uzunov - */ - -#ifndef INTERNAL_H_H -#define INTERNAL_H_H - -#include "platform.h" -#include "platform_interface.h" -#include "microspdy.h" - -/** - * size of read buffers for each connection - * must be at least the size of SPDY_MAX_SUPPORTED_FRAME_SIZE - */ -#define SPDYF_BUFFER_SIZE 8192 - -/** - * initial size of window for each stream (this is for the data - * within data frames that can be handled) - */ -#define SPDYF_INITIAL_WINDOW_SIZE 65536 - -/** - * number of frames written to the socket at once. After X frames - * everything should be run again. In this way the application can - * response to more important requests while a big file is still - * being transmitted to the client - */ -#define SPDYF_NUM_SENT_FRAMES_AT_ONCE 10 - - -/** - * Handler for fatal errors. - */ -extern SPDY_PanicCallback spdyf_panic; - - -/** - * Closure argument for "mhd_panic". - */ -extern void *spdyf_panic_cls; - - -/** - * Trigger 'panic' action based on fatal errors. - * - * @param msg error message (const char *) - */ -#define SPDYF_PANIC(msg) \ - spdyf_panic (spdyf_panic_cls, __FILE__, __LINE__, msg) - - -/** - * Asserts the validity of an expression. - * - * @param expr (bool) - * @param msg message to print on error (const char *) - */ -#define SPDYF_ASSERT(expr, msg) \ - if(!(expr)){\ - SPDYF_PANIC(msg);\ - abort();\ - } - - -/** - * Convert 24 bit integer from host byte order to network byte order. - * - * @param n input value (int32_t) - * @return converted value (uint32_t) - */ -#if HAVE_BIG_ENDIAN -#define HTON24(n) n -#else -#define HTON24(n) (((((uint32_t)(n) & 0xFF)) << 16)\ - | (((uint32_t)(n) & 0xFF00))\ - | ((((uint32_t)(n) & 0xFF0000)) >> 16)) -#endif - - -/** - * Convert 24 bit integer from network byte order to host byte order. - * - * @param n input value (int32_t) - * @return converted value (uint32_t) - */ -#if HAVE_BIG_ENDIAN -#define NTOH24(n) n -#else -#define NTOH24(n) (((((uint32_t)(n) & 0xFF)) << 16)\ - | (((uint32_t)(n) & 0xFF00))\ - | ((((uint32_t)(n) & 0xFF0000)) >> 16)) -#endif - - -/** - * Convert 31 bit integer from network byte order to host byte order. - * - * @param n input value (int32_t) - * @return converted value (uint32_t) - */ -#if HAVE_BIG_ENDIAN -#define NTOH31(n) n -#else -#define NTOH31(n) (((((uint32_t)(n) & 0x7F)) << 24) | \ - ((((uint32_t)(n) & 0xFF00)) << 8) | \ - ((((uint32_t)(n) & 0xFF0000)) >> 8) | \ - ((((uint32_t)(n) & 0xFF000000)) >> 24)) -#endif - - -/** - * Convert 31 bit integer from host byte order to network byte order. - * - * @param n input value (int32_t) - * @return converted value (uint32_t) - */ -#if HAVE_BIG_ENDIAN -#define HTON31(n) n -#else -#define HTON31(n) (((((uint32_t)(n) & 0xFF)) << 24) | \ - ((((uint32_t)(n) & 0xFF00)) << 8) | \ - ((((uint32_t)(n) & 0xFF0000)) >> 8) | \ - ((((uint32_t)(n) & 0x7F000000)) >> 24)) -#endif - - -/** - * Print formatted debug value. - * - * @param fmt format (const char *) - * @param ... args for format - */ -#define SPDYF_DEBUG(fmt, ...) do { \ - fprintf (stdout, "%s\n%u: ",__FILE__, __LINE__);\ - fprintf(stdout,fmt,##__VA_ARGS__);\ - fprintf(stdout,"\n");\ - fflush(stdout); } while (0) - - -/** - * Print stream for debuging. - * - * @param strm (void *) - * @param size (int) - */ -#define SPDYF_PRINT_STREAM(strm, size) do { \ - int ___i;\ - for(___i=0;___i<size;___i++){\ - fprintf(stdout,"%x ",*((uint8_t *) strm + ___i));\ - fflush(stdout);\ - }\ - fprintf(stdout,"\n");\ - } while (0) - - -/** - * Print message and raise SIGINT for debug purposes. - * - * @param msg message (const char *) - */ -#define SPDYF_SIGINT(msg) do { \ - fprintf(stdout,"%i : %s\n", __LINE__,__FILE__);\ - fprintf(stdout,msg);\ - fprintf(stdout,"\n");\ - fflush(stdout);\ - raise(SIGINT); } while (0) - - -/** - * Returns monotonic time, to be used for session timeouts. - * - * @return time in milliseconds - */ -unsigned long long -SPDYF_monotonic_time(void); - -#endif diff --git a/src/microspdy/io.c b/src/microspdy/io.c @@ -1,90 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file io.c - * @brief Generic functions for IO. - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "structures.h" -#include "internal.h" -#include "io.h" - - -int -SPDYF_io_set_daemon(struct SPDY_Daemon *daemon, - enum SPDY_IO_SUBSYSTEM io_subsystem) -{ - switch(io_subsystem) - { - case SPDY_IO_SUBSYSTEM_OPENSSL: - daemon->fio_init = &SPDYF_openssl_init; - daemon->fio_deinit = &SPDYF_openssl_deinit; - break; - - case SPDY_IO_SUBSYSTEM_RAW: - daemon->fio_init = &SPDYF_raw_init; - daemon->fio_deinit = &SPDYF_raw_deinit; - break; - - case SPDY_IO_SUBSYSTEM_NONE: - default: - SPDYF_DEBUG("Unsupported subsystem"); - return SPDY_NO; - } - - return SPDY_YES; -} - - -int -SPDYF_io_set_session(struct SPDY_Session *session, - enum SPDY_IO_SUBSYSTEM io_subsystem) -{ - switch(io_subsystem) - { - case SPDY_IO_SUBSYSTEM_OPENSSL: - session->fio_new_session = &SPDYF_openssl_new_session; - session->fio_close_session = &SPDYF_openssl_close_session; - session->fio_is_pending = &SPDYF_openssl_is_pending; - session->fio_recv = &SPDYF_openssl_recv; - session->fio_send = &SPDYF_openssl_send; - session->fio_before_write = &SPDYF_openssl_before_write; - session->fio_after_write = &SPDYF_openssl_after_write; - break; - - case SPDY_IO_SUBSYSTEM_RAW: - session->fio_new_session = &SPDYF_raw_new_session; - session->fio_close_session = &SPDYF_raw_close_session; - session->fio_is_pending = &SPDYF_raw_is_pending; - session->fio_recv = &SPDYF_raw_recv; - session->fio_send = &SPDYF_raw_send; - session->fio_before_write = &SPDYF_raw_before_write; - session->fio_after_write = &SPDYF_raw_after_write; - break; - - case SPDY_IO_SUBSYSTEM_NONE: - default: - SPDYF_DEBUG("Unsupported subsystem"); - return SPDY_NO; - } - - return SPDY_YES; -} diff --git a/src/microspdy/io.h b/src/microspdy/io.h @@ -1,216 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file io.h - * @brief Signatures for IO functions. - * @author Andrey Uzunov - */ - -#ifndef IO_H -#define IO_H - -#include "platform.h" -#include "io_openssl.h" -#include "io_raw.h" - - -/** - * Used for return code when reading and writing to the TLS socket. - */ -enum SPDY_IO_ERROR -{ - /** - * The connection was closed by the other party. - */ - SPDY_IO_ERROR_CLOSED = 0, - - /** - * Any kind of error ocurred. The session has to be closed. - */ - SPDY_IO_ERROR_ERROR = -2, - - /** - * The function had to return without processing any data. The whole - * cycle of events has to be called again (SPDY_run) as something - * either has to be written or read or the the syscall was - * interrupted by a signal. - */ - SPDY_IO_ERROR_AGAIN = -3, -}; - - -/** - * Global initializing. Must be called only once in the program. - * - */ -typedef void -(*SPDYF_IOGlobalInit) (); - - -/** - * Global deinitializing for the whole program. Should be called - * at the end of the program. - * - */ -typedef void -(*SPDYF_IOGlobalDeinit) (); - - -/** - * Initializing of io context for a specific daemon. - * Must be called when the daemon starts. - * - * @param daemon SPDY_Daemon for which io will be used. Daemon's - * certificate and key file are used for tls. - * @return SPDY_YES on success or SPDY_NO on error - */ -typedef int -(*SPDYF_IOInit) (struct SPDY_Daemon *daemon); - - -/** - * Deinitializing io context for a daemon. Should be called - * when the deamon is stopped. - * - * @param daemon SPDY_Daemon which is being stopped - */ -typedef void -(*SPDYF_IODeinit) (struct SPDY_Daemon *daemon); - - -/** - * Initializing io for a specific connection. Must be called - * after the connection has been accepted. - * - * @param session SPDY_Session whose socket will be used - * @return SPDY_NO if some funcs inside fail. SPDY_YES otherwise - */ -typedef int -(*SPDYF_IONewSession) (struct SPDY_Session *session); - - -/** - * Deinitializing io for a specific connection. Should be called - * closing session's socket. - * - * @param session SPDY_Session whose socket is used - */ -typedef void -(*SPDYF_IOCloseSession) (struct SPDY_Session *session); - - -/** - * Reading from session's socket. Reads available data and put it to the - * buffer. - * - * @param session for which data is received - * @param buffer where data from the socket will be written to - * @param size of the buffer - * @return number of bytes (at most size) read from the connection - * 0 if the other party has closed the connection - * SPDY_IO_ERROR code on error - */ -typedef int -(*SPDYF_IORecv) (struct SPDY_Session *session, - void * buffer, - size_t size); - - -/** - * Writing to session's socket. Writes the data given into the buffer to the - * socket. - * - * @param session whose context is used - * @param buffer from where data will be written to the socket - * @param size number of bytes to be taken from the buffer - * @return number of bytes (at most size) from the buffer that has been - * written to the connection - * 0 if the other party has closed the connection - * SPDY_IO_ERROR code on error - */ -typedef int -(*SPDYF_IOSend) (struct SPDY_Session *session, - const void * buffer, - size_t size); - - -/** - * Checks if there is data staying in the buffers of the underlying - * system that waits to be read. In case of TLS, this will call - * something like SSL_pending(). - * - * @param session which is checked - * @return SPDY_YES if data is pending or SPDY_NO otherwise - */ -typedef int -(*SPDYF_IOIsPending) (struct SPDY_Session *session); - - -/** - * Called just before frames are about to be processed and written - * to the socket. - * - * @param session - * @return SPDY_NO if writing must not happen in the call; - * SPDY_YES otherwise - */ -typedef int -(*SPDYF_IOBeforeWrite) (struct SPDY_Session *session); - - -/** - * Called just after frames have been processed and written - * to the socket. - * - * @param session - * @param was_written has the same value as the write function for the - * session will return - * @return returned value will be used by the write function to return - */ -typedef int -(*SPDYF_IOAfterWrite) (struct SPDY_Session *session, - int was_written); - - -/** - * Sets callbacks for the daemon with regard to the IO subsystem. - * - * @param daemon - * @param io_subsystem the IO subsystem that will - * be initialized and used by daemon. - * @return SPDY_YES on success or SPDY_NO otherwise - */ -int -SPDYF_io_set_daemon(struct SPDY_Daemon *daemon, - enum SPDY_IO_SUBSYSTEM io_subsystem); - - -/** - * Sets callbacks for the session with regard to the IO subsystem. - * - * @param session - * @param io_subsystem the IO subsystem that will - * be initialized and used by session. - * @return SPDY_YES on success or SPDY_NO otherwise - */ -int -SPDYF_io_set_session(struct SPDY_Session *session, - enum SPDY_IO_SUBSYSTEM io_subsystem); - -#endif diff --git a/src/microspdy/io_openssl.c b/src/microspdy/io_openssl.c @@ -1,280 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file io_openssl.c - * @brief TLS handling using libssl. The current code assumes that - * blocking I/O is in use. - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "internal.h" -#include "session.h" -#include "io_openssl.h" - - -/** - * Callback to advertise spdy ver. 3 in Next Protocol Negotiation - * - * @param ssl openssl context for a connection - * @param out must be set to the raw data that is advertised in NPN - * @param outlen must be set to size of out - * @param arg - * @return SSL_TLSEXT_ERR_OK to do advertising - */ -static int -spdyf_next_protos_advertised_cb (SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) -{ - (void)ssl; - (void)arg; - static unsigned char npn_spdy3[] = {0x06, // length of "spdy/3" - 0x73,0x70,0x64,0x79,0x2f,0x33};// spdy/3 - - *out = npn_spdy3; - *outlen = 7; // total length of npn_spdy3 - return SSL_TLSEXT_ERR_OK; -} - - -void -SPDYF_openssl_global_init() -{ - //error strings are now not used by the lib - //SSL_load_error_strings(); - //init libssl - SSL_library_init(); //always returns 1 - //the table for looking up algos is not used now by the lib - //OpenSSL_add_all_algorithms(); -} - - -void -SPDYF_openssl_global_deinit() -{ - //if SSL_load_error_strings was called - //ERR_free_strings(); - //if OpenSSL_add_all_algorithms was called - //EVP_cleanup(); -} - - -int -SPDYF_openssl_init(struct SPDY_Daemon *daemon) -{ - int options; - //create ssl context. TLSv1 used - if(NULL == (daemon->io_context = SSL_CTX_new(TLSv1_server_method()))) - { - SPDYF_DEBUG("Couldn't create ssl context"); - return SPDY_NO; - } - //set options for tls - //TODO DH is not enabled for easier debugging - //SSL_CTX_set_options(daemon->io_context, SSL_OP_SINGLE_DH_USE); - - //TODO here session tickets are disabled for easier debuging with - //wireshark when using Chrome - // SSL_OP_NO_COMPRESSION disables TLS compression to avoid CRIME attack - options = SSL_OP_NO_TICKET; -#ifdef SSL_OP_NO_COMPRESSION - options |= SSL_OP_NO_COMPRESSION; -#elif OPENSSL_VERSION_NUMBER >= 0x00908000L /* workaround for OpenSSL 0.9.8 */ - sk_SSL_COMP_zero(SSL_COMP_get_compression_methods()); -#endif - - SSL_CTX_set_options(daemon->io_context, options); - if(1 != SSL_CTX_use_certificate_file(daemon->io_context, daemon->certfile , SSL_FILETYPE_PEM)) - { - SPDYF_DEBUG("Couldn't load the cert file"); - SSL_CTX_free(daemon->io_context); - return SPDY_NO; - } - if(1 != SSL_CTX_use_PrivateKey_file(daemon->io_context, daemon->keyfile, SSL_FILETYPE_PEM)) - { - SPDYF_DEBUG("Couldn't load the name file"); - SSL_CTX_free(daemon->io_context); - return SPDY_NO; - } - SSL_CTX_set_next_protos_advertised_cb(daemon->io_context, &spdyf_next_protos_advertised_cb, NULL); - if (1 != SSL_CTX_set_cipher_list(daemon->io_context, "HIGH")) - { - SPDYF_DEBUG("Couldn't set the desired cipher list"); - SSL_CTX_free(daemon->io_context); - return SPDY_NO; - } - - return SPDY_YES; -} - - -void -SPDYF_openssl_deinit(struct SPDY_Daemon *daemon) -{ - SSL_CTX_free(daemon->io_context); -} - - -int -SPDYF_openssl_new_session(struct SPDY_Session *session) -{ - int ret; - - if(NULL == (session->io_context = SSL_new(session->daemon->io_context))) - { - SPDYF_DEBUG("Couldn't create ssl structure"); - return SPDY_NO; - } - if(1 != (ret = SSL_set_fd(session->io_context, session->socket_fd))) - { - SPDYF_DEBUG("SSL_set_fd %i",ret); - SSL_free(session->io_context); - session->io_context = NULL; - return SPDY_NO; - } - - //for non-blocking I/O SSL_accept may return -1 - //and this function won't work - if(1 != (ret = SSL_accept(session->io_context))) - { - SPDYF_DEBUG("SSL_accept %i",ret); - SSL_free(session->io_context); - session->io_context = NULL; - return SPDY_NO; - } - /* alternatively - SSL_set_accept_state(session->io_context); - * may be called and then the negotiation will be done on reading - */ - - return SPDY_YES; -} - - -void -SPDYF_openssl_close_session(struct SPDY_Session *session) -{ - //SSL_shutdown sends TLS "close notify" as in TLS standard. - //The function may fail as it waits for the other party to also close - //the TLS session. The lib just sends it and will close the socket - //after that because the browsers don't seem to care much about - //"close notify" - SSL_shutdown(session->io_context); - - SSL_free(session->io_context); -} - - -int -SPDYF_openssl_recv(struct SPDY_Session *session, - void * buffer, - size_t size) -{ - int ret; - int n = SSL_read(session->io_context, - buffer, - size); - //if(n > 0) SPDYF_DEBUG("recvd: %i",n); - if (n <= 0) - { - ret = SSL_get_error(session->io_context, n); - switch(ret) - { - case SSL_ERROR_ZERO_RETURN: - return 0; - - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - return SPDY_IO_ERROR_AGAIN; - - case SSL_ERROR_SYSCALL: - if(EINTR == errno) - return SPDY_IO_ERROR_AGAIN; - return SPDY_IO_ERROR_ERROR; - default: - return SPDY_IO_ERROR_ERROR; - } - } - - return n; -} - - -int -SPDYF_openssl_send(struct SPDY_Session *session, - const void * buffer, - size_t size) -{ - int ret; - - int n = SSL_write(session->io_context, - buffer, - size); - //if(n > 0) SPDYF_DEBUG("sent: %i",n); - if (n <= 0) - { - ret = SSL_get_error(session->io_context, n); - switch(ret) - { - case SSL_ERROR_ZERO_RETURN: - return 0; - - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - return SPDY_IO_ERROR_AGAIN; - - case SSL_ERROR_SYSCALL: - if(EINTR == errno) - return SPDY_IO_ERROR_AGAIN; - return SPDY_IO_ERROR_ERROR; - default: - return SPDY_IO_ERROR_ERROR; - } - } - - return n; -} - - -int -SPDYF_openssl_is_pending(struct SPDY_Session *session) -{ - /* From openssl docs: - * BUGS -SSL_pending() takes into account only bytes from the TLS/SSL record that is currently being processed (if any). If the SSL object's read_ahead flag is set, additional protocol bytes may have been read containing more TLS/SSL records; these are ignored by SSL_pending(). - */ - return SSL_pending(session->io_context) > 0 ? SPDY_YES : SPDY_NO; -} - - -int -SPDYF_openssl_before_write(struct SPDY_Session *session) -{ - (void)session; - - return SPDY_YES; -} - - -int -SPDYF_openssl_after_write(struct SPDY_Session *session, int was_written) -{ - (void)session; - - return was_written; -} diff --git a/src/microspdy/io_openssl.h b/src/microspdy/io_openssl.h @@ -1,166 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file io_openssl.h - * @brief TLS handling. openssl with NPN is used, but as long as the - * functions conform to this interface file, other libraries - * can be used. - * @author Andrey Uzunov - */ - -#ifndef IO_OPENSSL_H -#define IO_OPENSSL_H - -#include "platform.h" -#include "io.h" -#include <openssl/err.h> -#include <openssl/ssl.h> -#include <openssl/rand.h> - - -/** - * Global initializing of openssl. Must be called only once in the program. - * - */ -void -SPDYF_openssl_global_init(); - - -/** - * Global deinitializing of openssl for the whole program. Should be called - * at the end of the program. - * - */ -void -SPDYF_openssl_global_deinit(); - - -/** - * Initializing of openssl for a specific daemon. - * Must be called when the daemon starts. - * - * @param daemon SPDY_Daemon for which openssl will be used. Daemon's - * certificate and key file are used. - * @return SPDY_YES on success or SPDY_NO on error - */ -int -SPDYF_openssl_init(struct SPDY_Daemon *daemon); - - -/** - * Deinitializing openssl for a daemon. Should be called - * when the deamon is stopped. - * - * @param daemon SPDY_Daemon which is being stopped - */ -void -SPDYF_openssl_deinit(struct SPDY_Daemon *daemon); - - -/** - * Initializing openssl for a specific connection. Must be called - * after the connection has been accepted. - * - * @param session SPDY_Session whose socket will be used by openssl - * @return SPDY_NO if some openssl funcs fail. SPDY_YES otherwise - */ -int -SPDYF_openssl_new_session(struct SPDY_Session *session); - - -/** - * Deinitializing openssl for a specific connection. Should be called - * closing session's socket. - * - * @param session SPDY_Session whose socket is used by openssl - */ -void -SPDYF_openssl_close_session(struct SPDY_Session *session); - - -/** - * Reading from a TLS socket. Reads available data and put it to the - * buffer. - * - * @param session for which data is received - * @param buffer where data from the socket will be written to - * @param size of the buffer - * @return number of bytes (at most size) read from the TLS connection - * 0 if the other party has closed the connection - * SPDY_IO_ERROR code on error - */ -int -SPDYF_openssl_recv(struct SPDY_Session *session, - void * buffer, - size_t size); - - -/** - * Writing to a TLS socket. Writes the data given into the buffer to the - * TLS socket. - * - * @param session whose context is used - * @param buffer from where data will be written to the socket - * @param size number of bytes to be taken from the buffer - * @return number of bytes (at most size) from the buffer that has been - * written to the TLS connection - * 0 if the other party has closed the connection - * SPDY_IO_ERROR code on error - */ -int -SPDYF_openssl_send(struct SPDY_Session *session, - const void * buffer, - size_t size); - - -/** - * Checks if there is data staying in the buffers of the underlying - * system that waits to be read. - * - * @param session which is checked - * @return SPDY_YES if data is pending or SPDY_NO otherwise - */ -int -SPDYF_openssl_is_pending(struct SPDY_Session *session); - - -/** - * Nothing. - * - * @param session - * @return SPDY_NO if writing must not happen in the call; - * SPDY_YES otherwise - */ -int -SPDYF_openssl_before_write(struct SPDY_Session *session); - - -/** - * Nothing. - * - * @param session - * @param was_written has the same value as the write function for the - * session will return - * @return returned value will be used by the write function to return - */ -int -SPDYF_openssl_after_write(struct SPDY_Session *session, int was_written); - - -#endif diff --git a/src/microspdy/io_raw.c b/src/microspdy/io_raw.c @@ -1,194 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file io_raw.c - * @brief IO for SPDY without TLS. - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "internal.h" -#include "session.h" -#include "io_raw.h" -//TODO put in in the right place -#include <netinet/tcp.h> - - -void -SPDYF_raw_global_init() -{ -} - - -void -SPDYF_raw_global_deinit() -{ -} - - -int -SPDYF_raw_init(struct SPDY_Daemon *daemon) -{ - (void)daemon; - - return SPDY_YES; -} - - -void -SPDYF_raw_deinit(struct SPDY_Daemon *daemon) -{ - (void)daemon; -} - - -int -SPDYF_raw_new_session(struct SPDY_Session *session) -{ - int fd_flags; - int val = 1; - int ret; - - //setting the socket to be non-blocking - fd_flags = fcntl (session->socket_fd, F_GETFL); - if ( -1 == fd_flags - || 0 != fcntl (session->socket_fd, F_SETFL, fd_flags | O_NONBLOCK)) - SPDYF_DEBUG("WARNING: Couldn't set the new connection to be non-blocking"); - - if(SPDY_DAEMON_FLAG_NO_DELAY & session->daemon->flags) - { - ret = setsockopt(session->socket_fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); - if(-1 == ret) - SPDYF_DEBUG("WARNING: Couldn't set the new connection to TCP_NODELAY"); - } - - return SPDY_YES; -} - - -void -SPDYF_raw_close_session(struct SPDY_Session *session) -{ - (void)session; -} - - -int -SPDYF_raw_recv(struct SPDY_Session *session, - void * buffer, - size_t size) -{ - int n = read(session->socket_fd, - buffer, - size); - //if(n > 0) SPDYF_DEBUG("recvd: %i",n); - if (n < 0) - { - switch(errno) - { - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - case EINTR: - return SPDY_IO_ERROR_AGAIN; - - default: - return SPDY_IO_ERROR_ERROR; - } - } - - return n; -} - - -int -SPDYF_raw_send(struct SPDY_Session *session, - const void * buffer, - size_t size) -{ - int n = write(session->socket_fd, - buffer, - size); - //if(n > 0) SPDYF_DEBUG("sent: %i",n); - if (n < 0) - { - switch(errno) - { - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - case EINTR: - return SPDY_IO_ERROR_AGAIN; - - default: - return SPDY_IO_ERROR_ERROR; - } - } - - return n; -} - - -int -SPDYF_raw_is_pending(struct SPDY_Session *session) -{ - (void)session; - - return SPDY_NO; -} - - -int -SPDYF_raw_before_write(struct SPDY_Session *session) -{ -#if HAVE_DECL_TCP_CORK - if(0 == (SPDY_DAEMON_FLAG_NO_DELAY & session->daemon->flags)) - { - int val = 1; - int ret; - - ret = setsockopt(session->socket_fd, IPPROTO_TCP, TCP_CORK, &val, (socklen_t)sizeof(val)); - if(-1 == ret) - SPDYF_DEBUG("WARNING: Couldn't set the new connection to TCP_CORK"); - } -#endif - - return SPDY_YES; -} - - -int -SPDYF_raw_after_write(struct SPDY_Session *session, int was_written) -{ -#if HAVE_DECL_TCP_CORK - if(SPDY_YES == was_written && 0 == (SPDY_DAEMON_FLAG_NO_DELAY & session->daemon->flags)) - { - int val = 0; - int ret; - - ret = setsockopt(session->socket_fd, IPPROTO_TCP, TCP_CORK, &val, (socklen_t)sizeof(val)); - if(-1 == ret) - SPDYF_DEBUG("WARNING: Couldn't unset the new connection to TCP_CORK"); - } - -#endif - return was_written; -} diff --git a/src/microspdy/io_raw.h b/src/microspdy/io_raw.h @@ -1,158 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file io_raw.h - * @brief IO for SPDY without TLS. - * @author Andrey Uzunov - */ - -#ifndef IO_RAW_H -#define IO_RAW_H - -#include "platform.h" - - -/** - * Must be called only once in the program. - * - */ -void -SPDYF_raw_global_init(); - - -/** - * Should be called - * at the end of the program. - * - */ -void -SPDYF_raw_global_deinit(); - - -/** - * Must be called when the daemon starts. - * - * @param daemon SPDY_Daemon - * @return SPDY_YES on success or SPDY_NO on error - */ -int -SPDYF_raw_init(struct SPDY_Daemon *daemon); - - -/** - * Should be called - * when the deamon is stopped. - * - * @param daemon SPDY_Daemon which is being stopped - */ -void -SPDYF_raw_deinit(struct SPDY_Daemon *daemon); - - -/** - * Must be called - * after the connection has been accepted. - * - * @param session SPDY_Session whose socket will be used - * @return SPDY_NO if some funcs fail. SPDY_YES otherwise - */ -int -SPDYF_raw_new_session(struct SPDY_Session *session); - - -/** - * Should be called - * closing session's socket. - * - * @param session SPDY_Session whose socket is used - */ -void -SPDYF_raw_close_session(struct SPDY_Session *session); - - -/** - * Reading from socket. Reads available data and put it to the - * buffer. - * - * @param session for which data is received - * @param buffer where data from the socket will be written to - * @param size of the buffer - * @return number of bytes (at most size) read from the connection - * 0 if the other party has closed the connection - * SPDY_IO_ERROR code on error - */ -int -SPDYF_raw_recv(struct SPDY_Session *session, - void * buffer, - size_t size); - - -/** - * Writing to socket. Writes the data given into the buffer to the - * socket. - * - * @param session whose context is used - * @param buffer from where data will be written to the socket - * @param size number of bytes to be taken from the buffer - * @return number of bytes (at most size) from the buffer that has been - * written to the connection - * 0 if the other party has closed the connection - * SPDY_IO_ERROR code on error - */ -int -SPDYF_raw_send(struct SPDY_Session *session, - const void * buffer, - size_t size); - - -/** - * Checks if there is data staying in the buffers of the underlying - * system that waits to be read. Always returns SPDY_NO, as we do not - * use a subsystem here. - * - * @param session which is checked - * @return SPDY_YES if data is pending or SPDY_NO otherwise - */ -int -SPDYF_raw_is_pending(struct SPDY_Session *session); - - -/** - * Sets TCP_CORK. - * - * @param session - * @return SPDY_NO if writing must not happen in the call; - * SPDY_YES otherwise - */ -int -SPDYF_raw_before_write(struct SPDY_Session *session); - - -/** - * Unsets TCP_CORK. - * - * @param session - * @param was_written has the same value as the write function for the - * session will return - * @return returned value will be used by the write function to return - */ -int -SPDYF_raw_after_write(struct SPDY_Session *session, int was_written); - -#endif diff --git a/src/microspdy/session.c b/src/microspdy/session.c @@ -1,1769 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file session.c - * @brief TCP connection/SPDY session handling. So far most of the - * functions for handling SPDY framing layer are here. - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "structures.h" -#include "internal.h" -#include "session.h" -#include "compression.h" -#include "stream.h" -#include "io.h" - - -/** - * Handler for reading the full SYN_STREAM frame after we know that - * the frame is such. - * The function waits for the full frame and then changes status - * of the session. New stream is created. - * - * @param session SPDY_Session whose read buffer is used. - */ -static void -spdyf_handler_read_syn_stream (struct SPDY_Session *session) -{ - size_t name_value_strm_size = 0; - unsigned int compressed_data_size; - int ret; - void *name_value_strm = NULL; - struct SPDYF_Control_Frame *frame; - struct SPDY_NameValue *headers; - - SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status - || SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status, - "the function is called wrong"); - - frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls; - - //handle subheaders - if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status) - { - if(0 == frame->length) - { - //protocol error: incomplete frame - //we just ignore it since there is no stream id for which to - //send RST_STREAM - //TODO maybe GOAWAY and closing session is appropriate - SPDYF_DEBUG("zero long SYN_STREAM received"); - session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; - free(frame); - return; - } - - if(SPDY_YES != SPDYF_stream_new(session)) - { - /* waiting for some more fields to create new stream - or something went wrong, SPDYF_stream_new has handled the - situation */ - return; - } - - session->current_stream_id = session->streams_head->stream_id; - if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE) - { - //TODO no need to create stream if this happens - session->status = SPDY_SESSION_STATUS_IGNORE_BYTES; - return; - } - else - session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY; - } - - //handle body - - //start reading the compressed name/value pairs (http headers) - compressed_data_size = frame->length //everything after length field - - 10;//4B stream id, 4B assoc strem id, 2B priority, unused and slot - - if(session->read_buffer_offset - session->read_buffer_beginning < compressed_data_size) - { - // the full frame is not yet here, try later - return; - } - - if ( (compressed_data_size > 0) && - (SPDY_YES != - SPDYF_zlib_inflate(&session->zlib_recv_stream, - session->read_buffer + session->read_buffer_beginning, - compressed_data_size, - &name_value_strm, - &name_value_strm_size)) ) - { - /* something went wrong on inflating, - * the state of the stream for decompression is unknown - * and we may not be able to read anything more received on - * this session, - * so it is better to close the session */ - free(name_value_strm); - free(frame); - - /* mark the session for closing and close it, when - * everything on the output queue is already written */ - session->status = SPDY_SESSION_STATUS_FLUSHING; - - SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, false); - - return; - } - - if(0 == name_value_strm_size || 0 == compressed_data_size) - { - //Protocol error: send RST_STREAM - if(SPDY_YES != SPDYF_prepare_rst_stream(session, session->streams_head, - SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR)) - { - //no memory, try later to send RST - free(name_value_strm); - return; - } - } - else - { - ret = SPDYF_name_value_from_stream(name_value_strm, name_value_strm_size, &headers); - if(SPDY_NO == ret) - { - //memory error, try later - free(name_value_strm); - return; - } - - session->streams_head->headers = headers; - //inform the application layer for the new stream received - if(SPDY_YES != session->daemon->fnew_stream_cb(session->daemon->fcls, session->streams_head)) - { - //memory error, try later - free(name_value_strm); - return; - } - - session->read_buffer_beginning += compressed_data_size; - } - - //SPDYF_DEBUG("syn_stream received: id %i", session->current_stream_id); - - //change state to wait for new frame - free(name_value_strm); - session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; - free(frame); -} - - -/** - * Handler for reading the GOAWAY frame after we know that - * the frame is such. - * The function waits for the full frame and then changes status - * of the session. - * - * @param session SPDY_Session whose read buffer is used. - */ -static void -spdyf_handler_read_goaway (struct SPDY_Session *session) -{ - struct SPDYF_Control_Frame *frame; - uint32_t last_good_stream_id; - uint32_t status_int; - enum SPDY_GOAWAY_STATUS status; - - SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status, - "the function is called wrong"); - - frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls; - - if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE) - { - //this is a protocol error/attack - session->status = SPDY_SESSION_STATUS_IGNORE_BYTES; - return; - } - - if(0 != frame->flags || 8 != frame->length) - { - //this is a protocol error - SPDYF_DEBUG("wrong GOAWAY received"); - //anyway, it will be handled - } - - if((session->read_buffer_offset - session->read_buffer_beginning) < frame->length) - { - //not all fields are received - //try later - return; - } - - //mark that the session is almost closed - session->is_goaway_received = true; - - if(8 == frame->length) - { - memcpy(&last_good_stream_id, session->read_buffer + session->read_buffer_beginning, 4); - last_good_stream_id = NTOH31(last_good_stream_id); - session->read_buffer_beginning += 4; - - memcpy(&status_int, session->read_buffer + session->read_buffer_beginning, 4); - status = ntohl(status_int); - session->read_buffer_beginning += 4; - - //TODO do something with last_good - - //SPDYF_DEBUG("Received GOAWAY; status=%i; lastgood=%i",status,last_good_stream_id); - - //do something according to the status - //TODO - switch(status) - { - case SPDY_GOAWAY_STATUS_OK: - break; - case SPDY_GOAWAY_STATUS_PROTOCOL_ERROR: - break; - case SPDY_GOAWAY_STATUS_INTERNAL_ERROR: - break; - } - - //SPDYF_DEBUG("goaway received: status %i", status); - } - - session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; - free(frame); -} - - -/** - * Handler for reading RST_STREAM frames. After receiving the frame - * the stream moves into closed state and status - * of the session is changed. Frames, belonging to this stream, which - * are still at the output queue, will be ignored later. - * - * @param session SPDY_Session whose read buffer is used. - */ -static void -spdyf_handler_read_rst_stream (struct SPDY_Session *session) -{ - struct SPDYF_Control_Frame *frame; - uint32_t stream_id; - int32_t status_int; - //enum SPDY_RST_STREAM_STATUS status; //for debug - struct SPDYF_Stream *stream; - - SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status, - "the function is called wrong"); - - frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls; - - if(0 != frame->flags || 8 != frame->length) - { - //this is a protocol error - SPDYF_DEBUG("wrong RST_STREAM received"); - //ignore as a large frame - session->status = SPDY_SESSION_STATUS_IGNORE_BYTES; - return; - } - - if((session->read_buffer_offset - session->read_buffer_beginning) < frame->length) - { - //not all fields are received - //try later - return; - } - - memcpy(&stream_id, session->read_buffer + session->read_buffer_beginning, 4); - stream_id = NTOH31(stream_id); - session->read_buffer_beginning += 4; - - memcpy(&status_int, session->read_buffer + session->read_buffer_beginning, 4); - //status = ntohl(status_int); //for debug - session->read_buffer_beginning += 4; - - session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; - free(frame); - - //mark the stream as closed - stream = session->streams_head; - while(NULL != stream) - { - if(stream_id == stream->stream_id) - { - stream->is_in_closed = true; - stream->is_out_closed = true; - break; - } - stream = stream->next; - } - - //SPDYF_DEBUG("Received RST_STREAM; status=%i; id=%i",status,stream_id); - - //do something according to the status - //TODO - /*switch(status) - { - case SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR: - break; - }*/ -} - - -/** - * Handler for reading DATA frames. In requests they are used for POST - * arguments. - * - * @param session SPDY_Session whose read buffer is used. - */ -static void -spdyf_handler_read_data (struct SPDY_Session *session) -{ - int ret; - struct SPDYF_Data_Frame * frame; - struct SPDYF_Stream * stream; - - SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status - || SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status, - "the function is called wrong"); - - //SPDYF_DEBUG("DATA frame received (POST?). Ignoring"); - - //SPDYF_SIGINT(""); - - frame = (struct SPDYF_Data_Frame *)session->frame_handler_cls; - - //handle subheaders - if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status) - { - if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE) - { - session->status = SPDY_SESSION_STATUS_IGNORE_BYTES; - return; - } - else - session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY; - } - - //handle body - - if(session->read_buffer_offset - session->read_buffer_beginning - >= frame->length) - { - stream = SPDYF_stream_find(frame->stream_id, session); - - if(NULL == stream || stream->is_in_closed || NULL == session->daemon->received_data_cb) - { - if(NULL == session->daemon->received_data_cb) - SPDYF_DEBUG("No callback for DATA frame set; Ignoring DATA frame!"); - - //TODO send error? - - //TODO for now ignore frame - session->read_buffer_beginning += frame->length; - session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; - free(frame); - return; - } - - ret = session->daemon->freceived_data_cb(session->daemon->cls, - stream, - session->read_buffer + session->read_buffer_beginning, - frame->length, - 0 == (SPDY_DATA_FLAG_FIN & frame->flags)); - - session->read_buffer_beginning += frame->length; - - stream->window_size -= frame->length; - - //TODO close in and send rst maybe - SPDYF_ASSERT(SPDY_YES == ret, "Cancel POST data is not yet implemented"); - - if(SPDY_DATA_FLAG_FIN & frame->flags) - { - stream->is_in_closed = true; - } - else if(stream->window_size < SPDYF_INITIAL_WINDOW_SIZE / 2) - { - //very simple implementation of flow control - //when the window's size is under the half of the initial value, - //increase it again up to the initial value - - //prepare WINDOW_UPDATE - if(SPDY_YES == SPDYF_prepare_window_update(session, stream, - SPDYF_INITIAL_WINDOW_SIZE - stream->window_size)) - { - stream->window_size = SPDYF_INITIAL_WINDOW_SIZE; - } - //else: do it later - } - - //SPDYF_DEBUG("data received: id %i", frame->stream_id); - - session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; - free(frame); - } -} - - -int -SPDYF_handler_write_syn_reply (struct SPDY_Session *session) -{ - struct SPDYF_Response_Queue *response_queue = session->response_queue_head; - struct SPDYF_Stream *stream = response_queue->stream; - struct SPDYF_Control_Frame control_frame; - void *compressed_headers = NULL; - size_t compressed_headers_size=0; - size_t used_data=0; - size_t total_size; - uint32_t stream_id_nbo; - - SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment"); - - memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame)); - - if(SPDY_YES != SPDYF_zlib_deflate(&session->zlib_send_stream, - response_queue->data, - response_queue->data_size, - &used_data, - &compressed_headers, - &compressed_headers_size)) - { - /* something went wrong on compressing, - * the state of the stream for compression is unknown - * and we may not be able to send anything more on - * this session, - * so it is better to close the session right now */ - session->status = SPDY_SESSION_STATUS_CLOSING; - - free(compressed_headers); - - return SPDY_NO; - } - - //TODO do we need this used_Data - SPDYF_ASSERT(used_data == response_queue->data_size, "not everything was used by zlib"); - - total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header - + 4 // stream id as "subheader" - + compressed_headers_size; - - if(NULL == (session->write_buffer = malloc(total_size))) - { - /* no memory - * since we do not save the compressed data anywhere and - * the sending zlib stream is already in new state, we must - * close the session */ - session->status = SPDY_SESSION_STATUS_CLOSING; - - free(compressed_headers); - - return SPDY_NO; - } - session->write_buffer_beginning = 0; - session->write_buffer_offset = 0; - session->write_buffer_size = total_size; - - control_frame.length = compressed_headers_size + 4; // compressed data + stream_id - SPDYF_CONTROL_FRAME_HTON(&control_frame); - - //put frame headers to write buffer - memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame)); - session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame); - - //put stream id to write buffer - stream_id_nbo = HTON31(stream->stream_id); - memcpy(session->write_buffer + session->write_buffer_offset, &stream_id_nbo, 4); - session->write_buffer_offset += 4; - - //put compressed name/value pairs to write buffer - memcpy(session->write_buffer + session->write_buffer_offset, compressed_headers, compressed_headers_size); - session->write_buffer_offset += compressed_headers_size; - - SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1"); - SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2"); - - //DEBUG CODE, break compression state to see what happens -/* SPDYF_zlib_deflate(&session->zlib_send_stream, - "1234567890", - 10, - &used_data, - &compressed_headers, - &compressed_headers_size); -*/ - free(compressed_headers); - - session->last_replied_to_stream_id = stream->stream_id; - - //SPDYF_DEBUG("syn_reply sent: id %i", stream->stream_id); - - return SPDY_YES; -} - - -int -SPDYF_handler_write_goaway (struct SPDY_Session *session) -{ - struct SPDYF_Response_Queue *response_queue = session->response_queue_head; - struct SPDYF_Control_Frame control_frame; - size_t total_size; - int last_good_stream_id; - - SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment"); - - memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame)); - - session->is_goaway_sent = true; - - total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header - + 4 // last good stream id as "subheader" - + 4; // status code as "subheader" - - if(NULL == (session->write_buffer = malloc(total_size))) - { - return SPDY_NO; - } - session->write_buffer_beginning = 0; - session->write_buffer_offset = 0; - session->write_buffer_size = total_size; - - control_frame.length = 8; // always for GOAWAY - SPDYF_CONTROL_FRAME_HTON(&control_frame); - - //put frame headers to write buffer - memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame)); - session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame); - - //put last good stream id to write buffer - last_good_stream_id = HTON31(session->last_replied_to_stream_id); - memcpy(session->write_buffer + session->write_buffer_offset, &last_good_stream_id, 4); - session->write_buffer_offset += 4; - - //put "data" to write buffer. This is the status - memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 4); - session->write_buffer_offset += 4; - //data is not freed by the destroy function so: - //free(response_queue->data); - - //SPDYF_DEBUG("goaway sent: status %i", NTOH31(*(uint32_t*)(response_queue->data))); - - SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1"); - SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2"); - - return SPDY_YES; -} - - -int -SPDYF_handler_write_data (struct SPDY_Session *session) -{ - struct SPDYF_Response_Queue *response_queue = session->response_queue_head; - struct SPDYF_Response_Queue *new_response_queue; - size_t total_size; - struct SPDYF_Data_Frame data_frame; - ssize_t ret; - bool more; - - SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment"); - - memcpy(&data_frame, response_queue->data_frame, sizeof(data_frame)); - - if(NULL == response_queue->response->rcb) - { - //standard response with data into the struct - SPDYF_ASSERT(NULL != response_queue->data, "no data for the response"); - - total_size = sizeof(struct SPDYF_Data_Frame) //SPDY header - + response_queue->data_size; - - if(NULL == (session->write_buffer = malloc(total_size))) - { - return SPDY_NO; - } - session->write_buffer_beginning = 0; - session->write_buffer_offset = 0; - session->write_buffer_size = total_size; - - data_frame.length = response_queue->data_size; - SPDYF_DATA_FRAME_HTON(&data_frame); - - //put SPDY headers to the writing buffer - memcpy(session->write_buffer + session->write_buffer_offset,&data_frame,sizeof(struct SPDYF_Data_Frame)); - session->write_buffer_offset += sizeof(struct SPDYF_Data_Frame); - - //put data to the writing buffer - memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, response_queue->data_size); - session->write_buffer_offset += response_queue->data_size; - } - else - { - /* response with callbacks. The lib will produce more than 1 - * data frames - */ - - total_size = sizeof(struct SPDYF_Data_Frame) //SPDY header - + SPDY_MAX_SUPPORTED_FRAME_SIZE; //max possible size - - if(NULL == (session->write_buffer = malloc(total_size))) - { - return SPDY_NO; - } - session->write_buffer_beginning = 0; - session->write_buffer_offset = 0; - session->write_buffer_size = total_size; - - ret = response_queue->response->rcb(response_queue->response->rcb_cls, - session->write_buffer + sizeof(struct SPDYF_Data_Frame), - response_queue->response->rcb_block_size, - &more); - - if(ret < 0 || ret > response_queue->response->rcb_block_size) - { - free(session->write_buffer); - session->write_buffer = NULL; - - //send RST_STREAM - if(SPDY_YES == (ret = SPDYF_prepare_rst_stream(session, - response_queue->stream, - SPDY_RST_STREAM_STATUS_INTERNAL_ERROR))) - { - return SPDY_NO; - } - - //else no memory - //for now close session - //TODO what? - session->status = SPDY_SESSION_STATUS_CLOSING; - - return SPDY_NO; - } - if(0 == ret && more) - { - //the app couldn't write anything to buf but later will - free(session->write_buffer); - session->write_buffer = NULL; - session->write_buffer_size = 0; - - if(NULL != response_queue->next) - { - //put the frame at the end of the queue - //otherwise - head of line blocking - session->response_queue_head = response_queue->next; - session->response_queue_head->prev = NULL; - session->response_queue_tail->next = response_queue; - response_queue->prev = session->response_queue_tail; - response_queue->next = NULL; - session->response_queue_tail = response_queue; - } - - return SPDY_YES; - } - - if(more) - { - //create another response queue object to call the user cb again - if(NULL == (new_response_queue = SPDYF_response_queue_create(true, - NULL, - 0, - response_queue->response, - response_queue->stream, - false, - response_queue->frqcb, - response_queue->frqcb_cls, - response_queue->rrcb, - response_queue->rrcb_cls))) - { - //TODO send RST_STREAM - //for now close session - session->status = SPDY_SESSION_STATUS_CLOSING; - - free(session->write_buffer); - session->write_buffer = NULL; - return SPDY_NO; - } - - //put it at second position on the queue - new_response_queue->prev = response_queue; - new_response_queue->next = response_queue->next; - if(NULL == response_queue->next) - { - session->response_queue_tail = new_response_queue; - } - else - { - response_queue->next->prev = new_response_queue; - } - response_queue->next = new_response_queue; - - response_queue->frqcb = NULL; - response_queue->frqcb_cls = NULL; - response_queue->rrcb = NULL; - response_queue->rrcb_cls = NULL; - } - else - { - data_frame.flags |= SPDY_DATA_FLAG_FIN; - } - - data_frame.length = ret; - SPDYF_DATA_FRAME_HTON(&data_frame); - - //put SPDY headers to the writing buffer - memcpy(session->write_buffer + session->write_buffer_offset, - &data_frame, - sizeof(struct SPDYF_Data_Frame)); - session->write_buffer_offset += sizeof(struct SPDYF_Data_Frame); - session->write_buffer_offset += ret; - session->write_buffer_size = session->write_buffer_offset; - } - - //SPDYF_DEBUG("data sent: id %i", NTOH31(data_frame.stream_id)); - - SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1"); - SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2"); - - return SPDY_YES; -} - - -int -SPDYF_handler_write_rst_stream (struct SPDY_Session *session) -{ - struct SPDYF_Response_Queue *response_queue = session->response_queue_head; - struct SPDYF_Control_Frame control_frame; - size_t total_size; - - SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment"); - - memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame)); - - total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header - + 4 // stream id as "subheader" - + 4; // status code as "subheader" - - if(NULL == (session->write_buffer = malloc(total_size))) - { - return SPDY_NO; - } - session->write_buffer_beginning = 0; - session->write_buffer_offset = 0; - session->write_buffer_size = total_size; - - control_frame.length = 8; // always for RST_STREAM - SPDYF_CONTROL_FRAME_HTON(&control_frame); - - //put frame headers to write buffer - memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame)); - session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame); - - //put stream id to write buffer. This is the status - memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 8); - session->write_buffer_offset += 8; - //data is not freed by the destroy function so: - //free(response_queue->data); - - //SPDYF_DEBUG("rst_stream sent: id %i", NTOH31((((uint64_t)response_queue->data) & 0xFFFF0000) >> 32)); - - SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1"); - SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2"); - - return SPDY_YES; -} - - -int -SPDYF_handler_write_window_update (struct SPDY_Session *session) -{ - struct SPDYF_Response_Queue *response_queue = session->response_queue_head; - struct SPDYF_Control_Frame control_frame; - size_t total_size; - - SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment"); - - memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame)); - - total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header - + 4 // stream id as "subheader" - + 4; // delta-window-size as "subheader" - - if(NULL == (session->write_buffer = malloc(total_size))) - { - return SPDY_NO; - } - session->write_buffer_beginning = 0; - session->write_buffer_offset = 0; - session->write_buffer_size = total_size; - - control_frame.length = 8; // always for WINDOW_UPDATE - SPDYF_CONTROL_FRAME_HTON(&control_frame); - - //put frame headers to write buffer - memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame)); - session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame); - - //put stream id and delta-window-size to write buffer - memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 8); - session->write_buffer_offset += 8; - - //SPDYF_DEBUG("window_update sent: id %i", NTOH31((((uint64_t)response_queue->data) & 0xFFFF0000) >> 32)); - - SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1"); - SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2"); - - return SPDY_YES; -} - - -void -SPDYF_handler_ignore_frame (struct SPDY_Session *session) -{ - struct SPDYF_Control_Frame *frame; - - SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status - || SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status, - "the function is called wrong"); - - - frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls; - - //handle subheaders - if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status) - { - if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE) - { - session->status = SPDY_SESSION_STATUS_IGNORE_BYTES; - return; - } - else - session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY; - } - - //handle body - - if(session->read_buffer_offset - session->read_buffer_beginning - >= frame->length) - { - session->read_buffer_beginning += frame->length; - session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; - free(frame); - } -} - - -int -SPDYF_session_read (struct SPDY_Session *session) -{ - int bytes_read; - bool reallocate; - size_t actual_buf_size; - - if(SPDY_SESSION_STATUS_CLOSING == session->status - || SPDY_SESSION_STATUS_FLUSHING == session->status) - return SPDY_NO; - - //if the read buffer is full to the end, we need to reallocate space - if (session->read_buffer_size == session->read_buffer_offset) - { - //but only if the state of the session requires it - //i.e. no further proceeding is possible without reallocation - reallocate = false; - actual_buf_size = session->read_buffer_offset - - session->read_buffer_beginning; - switch(session->status) - { - case SPDY_SESSION_STATUS_WAIT_FOR_HEADER: - - case SPDY_SESSION_STATUS_IGNORE_BYTES: - //we need space for a whole control frame header - if(actual_buf_size < sizeof(struct SPDYF_Control_Frame)) - reallocate = true; - break; - - case SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER: - - case SPDY_SESSION_STATUS_WAIT_FOR_BODY: - //we need as many bytes as set in length field of the - //header - SPDYF_ASSERT(NULL != session->frame_handler_cls, - "no frame for session"); - if(session->frame_handler != &spdyf_handler_read_data) - { - if(actual_buf_size - < ((struct SPDYF_Control_Frame *)session->frame_handler_cls)->length) - reallocate = true; - } - else - { - if(actual_buf_size - < ((struct SPDYF_Data_Frame *)session->frame_handler_cls)->length) - reallocate = true; - } - break; - - case SPDY_SESSION_STATUS_CLOSING: - case SPDY_SESSION_STATUS_FLUSHING: - //nothing needed - break; - } - - if(reallocate) - { - //reuse the space in the buffer that was already read by the lib - memmove(session->read_buffer, - session->read_buffer + session->read_buffer_beginning, - session->read_buffer_offset - session->read_buffer_beginning); - - session->read_buffer_offset -= session->read_buffer_beginning; - session->read_buffer_beginning = 0; - } - else - { - //will read next time - //TODO optimize it, memmove more often? - return SPDY_NO; - } - } - - session->last_activity = SPDYF_monotonic_time(); - - //actual read from the TLS socket - bytes_read = session->fio_recv(session, - session->read_buffer + session->read_buffer_offset, - session->read_buffer_size - session->read_buffer_offset); - - switch(bytes_read) - { - case SPDY_IO_ERROR_CLOSED: - //The TLS connection was closed by the other party, clean - //or not - shutdown (session->socket_fd, SHUT_RD); - session->read_closed = true; - session->status = SPDY_SESSION_STATUS_CLOSING; - return SPDY_YES; - - case SPDY_IO_ERROR_ERROR: - //any kind of error in the TLS subsystem - //try to prepare GOAWAY frame - SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, false); - //try to flush the queue when write is called - session->status = SPDY_SESSION_STATUS_FLUSHING; - return SPDY_YES; - - case SPDY_IO_ERROR_AGAIN: - //read or write should be called again; leave it for the - //next time - return SPDY_NO; - - //default: - //something was really read from the TLS subsystem - //just continue - } - - session->read_buffer_offset += bytes_read; - - return SPDY_YES; -} - - -int -SPDYF_session_write (struct SPDY_Session *session, - bool only_one_frame) -{ - unsigned int i; - int bytes_written; - struct SPDYF_Response_Queue *queue_head; - struct SPDYF_Response_Queue *response_queue; - - if(SPDY_SESSION_STATUS_CLOSING == session->status) - return SPDY_NO; - - if(SPDY_NO == session->fio_before_write(session)) - return SPDY_NO; - - for(i=0; - only_one_frame - ? i < 1 - : i < session->max_num_frames; - ++i) - { - //if the buffer is not null, part of the last frame is still - //pending to be sent - if(NULL == session->write_buffer) - { - //discard frames on closed streams - response_queue = session->response_queue_head; - - while(NULL != response_queue) - { - //if stream is closed, remove not yet sent frames - //associated with it - //GOAWAY frames are not associated to streams - //and still need to be sent - if(NULL == response_queue->stream - || !response_queue->stream->is_out_closed) - break; - - DLL_remove(session->response_queue_head,session->response_queue_tail,response_queue); - - if(NULL != response_queue->frqcb) - { - response_queue->frqcb(response_queue->frqcb_cls, response_queue, SPDY_RESPONSE_RESULT_STREAM_CLOSED); - } - - SPDYF_response_queue_destroy(response_queue); - response_queue = session->response_queue_head; - } - - if(NULL == session->response_queue_head) - break;//nothing on the queue - - //get next data from queue and put it to the write buffer - // to send it - if(SPDY_NO == session->response_queue_head->process_response_handler(session)) - { - //error occured and the handler changed or not the - //session's status appropriately - if(SPDY_SESSION_STATUS_CLOSING == session->status) - { - //try to send GOAWAY first if the current frame is different - if(session->response_queue_head->is_data - || SPDY_CONTROL_FRAME_TYPES_GOAWAY - != session->response_queue_head->control_frame->type) - { - session->status = SPDY_SESSION_STATUS_FLUSHING; - SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, true); - SPDYF_session_write(session,true); - session->status = SPDY_SESSION_STATUS_CLOSING; - } - return SPDY_YES; - } - - //just return from the loop to return from this function - ++i; - break; - } - - //check if something was prepared for writing - //on respones with callbacks it is possible that their is no - //data available - if(0 == session->write_buffer_size)//nothing to write - { - if(response_queue != session->response_queue_head) - { - //the handler modified the queue - continue; - } - else - { - //no need to try the same frame again - ++i; - break; - } - } - } - - session->last_activity = SPDYF_monotonic_time(); - - //actual write to the IO - bytes_written = session->fio_send(session, - session->write_buffer + session->write_buffer_beginning, - session->write_buffer_offset - session->write_buffer_beginning); - - switch(bytes_written) - { - case SPDY_IO_ERROR_CLOSED: - //The TLS connection was closed by the other party, clean - //or not - shutdown (session->socket_fd, SHUT_RD); - session->read_closed = true; - session->status = SPDY_SESSION_STATUS_CLOSING; - return SPDY_YES; - - case SPDY_IO_ERROR_ERROR: - //any kind of error in the TLS subsystem - //forbid more writing - session->status = SPDY_SESSION_STATUS_CLOSING; - return SPDY_YES; - - case SPDY_IO_ERROR_AGAIN: - //read or write should be called again; leave it for the - //next time; return from the function as we do not now - //whether reading or writing is needed - return i>0 ? SPDY_YES : SPDY_NO; - - //default: - //something was really read from the TLS subsystem - //just continue - } - - session->write_buffer_beginning += bytes_written; - - //check if the full buffer was written - if(session->write_buffer_beginning == session->write_buffer_size) - { - //that response is handled, remove it from queue - free(session->write_buffer); - session->write_buffer = NULL; - session->write_buffer_size = 0; - queue_head = session->response_queue_head; - if(NULL == queue_head->next) - { - session->response_queue_head = NULL; - session->response_queue_tail = NULL; - } - else - { - session->response_queue_head = queue_head->next; - session->response_queue_head->prev = NULL; - } - - //set stream to closed if the frame's fin flag is set - SPDYF_stream_set_flags_on_write(queue_head); - - if(NULL != queue_head->frqcb) - { - //application layer callback to notify sending of the response - queue_head->frqcb(queue_head->frqcb_cls, queue_head, SPDY_RESPONSE_RESULT_SUCCESS); - } - - SPDYF_response_queue_destroy(queue_head); - } - } - - if(SPDY_SESSION_STATUS_FLUSHING == session->status - && NULL == session->response_queue_head) - session->status = SPDY_SESSION_STATUS_CLOSING; - - //return i>0 ? SPDY_YES : SPDY_NO; - return session->fio_after_write(session, i>0 ? SPDY_YES : SPDY_NO); -} - - -int -SPDYF_session_idle (struct SPDY_Session *session) -{ - size_t read_buffer_beginning; - size_t frame_length; - struct SPDYF_Control_Frame* control_frame; - struct SPDYF_Data_Frame *data_frame; - - //prepare session for closing if timeout is used and already passed - if(SPDY_SESSION_STATUS_CLOSING != session->status - && session->daemon->session_timeout - && (session->last_activity + session->daemon->session_timeout < SPDYF_monotonic_time())) - { - session->status = SPDY_SESSION_STATUS_CLOSING; - //best effort for sending GOAWAY - SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_OK, true); - SPDYF_session_write(session,true); - } - - switch(session->status) - { - //expect new frame to arrive - case SPDY_SESSION_STATUS_WAIT_FOR_HEADER: - session->current_stream_id = 0; - //check if the whole frame header is already here - //both frame types have the same length - if(session->read_buffer_offset - session->read_buffer_beginning - < sizeof(struct SPDYF_Control_Frame)) - return SPDY_NO; - - /* check the first bit to see if it is data or control frame - * and also if the version is supported */ - if(0x80 == *(uint8_t *)(session->read_buffer + session->read_buffer_beginning) - && SPDY_VERSION == *((uint8_t *)session->read_buffer + session->read_buffer_beginning + 1)) - { - //control frame - if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame)))) - { - SPDYF_DEBUG("No memory"); - return SPDY_NO; - } - - //get frame headers - memcpy(control_frame, - session->read_buffer + session->read_buffer_beginning, - sizeof(struct SPDYF_Control_Frame)); - session->read_buffer_beginning += sizeof(struct SPDYF_Control_Frame); - SPDYF_CONTROL_FRAME_NTOH(control_frame); - - session->status = SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER; - //assign different frame handler according to frame type - switch(control_frame->type){ - case SPDY_CONTROL_FRAME_TYPES_SYN_STREAM: - session->frame_handler = &spdyf_handler_read_syn_stream; - break; - case SPDY_CONTROL_FRAME_TYPES_GOAWAY: - session->frame_handler = &spdyf_handler_read_goaway; - break; - case SPDY_CONTROL_FRAME_TYPES_RST_STREAM: - session->frame_handler = &spdyf_handler_read_rst_stream; - break; - default: - session->frame_handler = &SPDYF_handler_ignore_frame; - } - session->frame_handler_cls = control_frame; - //DO NOT break the outer case - } - else if(0 == *(uint8_t *)(session->read_buffer + session->read_buffer_beginning)) - { - //needed for POST - //data frame - if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame)))) - { - SPDYF_DEBUG("No memory"); - return SPDY_NO; - } - - //get frame headers - memcpy(data_frame, - session->read_buffer + session->read_buffer_beginning, - sizeof(struct SPDYF_Data_Frame)); - session->read_buffer_beginning += sizeof(struct SPDYF_Data_Frame); - SPDYF_DATA_FRAME_NTOH(data_frame); - - session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY; - session->frame_handler = &spdyf_handler_read_data; - session->frame_handler_cls = data_frame; - //DO NOT brake the outer case - } - else - { - SPDYF_DEBUG("another protocol or version received!"); - - /* According to the draft the lib should send here - * RST_STREAM with status UNSUPPORTED_VERSION. I don't - * see any sense of keeping the session open since - * we don't know how many bytes is the bogus "frame". - * And the latter normally will be HTTP request. - * - */ - - //shutdown(session->socket_fd, SHUT_RD); - session->status = SPDY_SESSION_STATUS_FLUSHING; - SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_PROTOCOL_ERROR,false); - //SPDYF_session_write(session,false); - /* close connection since the client expects another - protocol from us */ - //SPDYF_session_close(session); - return SPDY_YES; - } - - //expect specific header fields after the standard header - case SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER: - if(NULL!=session->frame_handler) - { - read_buffer_beginning = session->read_buffer_beginning; - //if everything is ok, the "body" will also be processed - //by the handler - session->frame_handler(session); - - if(SPDY_SESSION_STATUS_IGNORE_BYTES == session->status) - { - //check for larger than max supported frame - if(session->frame_handler != &spdyf_handler_read_data) - { - frame_length = ((struct SPDYF_Control_Frame *)session->frame_handler_cls)->length; - } - else - { - frame_length = ((struct SPDYF_Data_Frame *)session->frame_handler_cls)->length; - } - - //if(SPDY_MAX_SUPPORTED_FRAME_SIZE < frame_length) - { - SPDYF_DEBUG("received frame with unsupported size: %zu", frame_length); - //the data being received must be ignored and - //RST_STREAM sent - - //ignore bytes that will arive later - session->read_ignore_bytes = frame_length - + read_buffer_beginning - - session->read_buffer_offset; - //ignore what is already in read buffer - session->read_buffer_beginning = session->read_buffer_offset; - - SPDYF_prepare_rst_stream(session, - session->current_stream_id > 0 ? session->streams_head : NULL, //may be 0 here which is not good - SPDY_RST_STREAM_STATUS_FRAME_TOO_LARGE); - - //actually the read buffer can be bigger than the - //max supported size - session->status = session->read_ignore_bytes - ? SPDY_SESSION_STATUS_IGNORE_BYTES - : SPDY_SESSION_STATUS_WAIT_FOR_HEADER; - - free(session->frame_handler_cls); - } - } - } - - if(SPDY_SESSION_STATUS_IGNORE_BYTES != session->status) - { - break; - } - - //ignoring data in read buffer - case SPDY_SESSION_STATUS_IGNORE_BYTES: - SPDYF_ASSERT(session->read_ignore_bytes > 0, - "Session is in wrong state"); - if(session->read_ignore_bytes - > session->read_buffer_offset - session->read_buffer_beginning) - { - session->read_ignore_bytes -= - session->read_buffer_offset - session->read_buffer_beginning; - session->read_buffer_beginning = session->read_buffer_offset; - } - else - { - session->read_buffer_beginning += session->read_ignore_bytes; - session->read_ignore_bytes = 0; - session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; - } - break; - - //expect frame body (name/value pairs) - case SPDY_SESSION_STATUS_WAIT_FOR_BODY: - if(NULL!=session->frame_handler) - session->frame_handler(session); - break; - - case SPDY_SESSION_STATUS_FLUSHING: - - return SPDY_NO; - - //because of error the session needs to be closed - case SPDY_SESSION_STATUS_CLOSING: - //error should be already sent to the client - SPDYF_session_close(session); - return SPDY_YES; - } - - return SPDY_YES; -} - - -void -SPDYF_session_close (struct SPDY_Session *session) -{ - struct SPDY_Daemon *daemon = session->daemon; - int by_client = session->read_closed ? SPDY_YES : SPDY_NO; - - //shutdown the tls and deinit the tls context - session->fio_close_session(session); - shutdown (session->socket_fd, - session->read_closed ? SHUT_WR : SHUT_RDWR); - session->read_closed = true; - - //remove session from the list - DLL_remove (daemon->sessions_head, - daemon->sessions_tail, - session); - //add the session for the list for cleaning up - DLL_insert (daemon->cleanup_head, - daemon->cleanup_tail, - session); - - //call callback for closed session - if(NULL != daemon->session_closed_cb) - { - daemon->session_closed_cb(daemon->cls, session, by_client); - } -} - - -int -SPDYF_session_accept(struct SPDY_Daemon *daemon) -{ - int new_socket_fd; - int ret; - struct SPDY_Session *session = NULL; - socklen_t addr_len; - struct sockaddr *addr; - -#if HAVE_INET6 - struct sockaddr_in6 addr6; - - addr = (struct sockaddr *)&addr6; - addr_len = sizeof(addr6); -#else - struct sockaddr_in addr4; - - addr = (struct sockaddr *)&addr4; - addr_len = sizeof(addr6); -#endif - - new_socket_fd = accept (daemon->socket_fd, addr, &addr_len); - - if(new_socket_fd < 1) - return SPDY_NO; - - if (NULL == (session = malloc (sizeof (struct SPDY_Session)))) - { - goto free_and_fail; - } - memset (session, 0, sizeof (struct SPDY_Session)); - - session->daemon = daemon; - session->socket_fd = new_socket_fd; - session->max_num_frames = daemon->max_num_frames; - - ret = SPDYF_io_set_session(session, daemon->io_subsystem); - SPDYF_ASSERT(SPDY_YES == ret, "Somehow daemon->io_subsystem iswrong here"); - - //init TLS context, handshake will be done - if(SPDY_YES != session->fio_new_session(session)) - { - goto free_and_fail; - } - - //read buffer - session->read_buffer_size = SPDYF_BUFFER_SIZE; - if (NULL == (session->read_buffer = malloc (session->read_buffer_size))) - { - session->fio_close_session(session); - goto free_and_fail; - } - - //address of the client - if (NULL == (session->addr = malloc (addr_len))) - { - session->fio_close_session(session); - goto free_and_fail; - } - memcpy (session->addr, addr, addr_len); - - session->addr_len = addr_len; - session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; - - //init zlib context for the whole session - if(SPDY_YES != SPDYF_zlib_deflate_init(&session->zlib_send_stream)) - { - session->fio_close_session(session); - goto free_and_fail; - } - if(SPDY_YES != SPDYF_zlib_inflate_init(&session->zlib_recv_stream)) - { - session->fio_close_session(session); - SPDYF_zlib_deflate_end(&session->zlib_send_stream); - goto free_and_fail; - } - - //add it to daemon's list - DLL_insert(daemon->sessions_head,daemon->sessions_tail,session); - - session->last_activity = SPDYF_monotonic_time(); - - if(NULL != daemon->new_session_cb) - daemon->new_session_cb(daemon->cls, session); - - return SPDY_YES; - - //for GOTO - free_and_fail: - /* something failed, so shutdown, close and free memory */ - shutdown (new_socket_fd, SHUT_RDWR); - (void)MHD_socket_close_ (new_socket_fd); - - if(NULL != session) - { - if(NULL != session->addr) - free (session->addr); - if(NULL != session->read_buffer) - free (session->read_buffer); - free (session); - } - return SPDY_NO; -} - - -void -SPDYF_queue_response (struct SPDYF_Response_Queue *response_to_queue, - struct SPDY_Session *session, - int consider_priority) -{ - struct SPDYF_Response_Queue *pos; - struct SPDYF_Response_Queue *last; - uint8_t priority; - - SPDYF_ASSERT(SPDY_YES != consider_priority || NULL != response_to_queue->stream, - "called with consider_priority but no stream provided"); - - last = response_to_queue; - while(NULL != last->next) - { - last = last->next; - } - - if(SPDY_NO == consider_priority) - { - //put it at the end of the queue - response_to_queue->prev = session->response_queue_tail; - if (NULL == session->response_queue_head) - session->response_queue_head = response_to_queue; - else - session->response_queue_tail->next = response_to_queue; - session->response_queue_tail = last; - return; - } - else if(-1 == consider_priority) - { - //put it at the head of the queue - last->next = session->response_queue_head; - if (NULL == session->response_queue_tail) - session->response_queue_tail = last; - else - session->response_queue_head->prev = response_to_queue; - session->response_queue_head = response_to_queue; - return; - } - - if(NULL == session->response_queue_tail) - { - session->response_queue_head = response_to_queue; - session->response_queue_tail = last; - return; - } - - //search for the right position to put it - pos = session->response_queue_tail; - priority = response_to_queue->stream->priority; - while(NULL != pos - && pos->stream->priority > priority) - { - pos = pos->prev; - } - - if(NULL == pos) - { - //put it on the head - session->response_queue_head->prev = last; - last->next = session->response_queue_head; - session->response_queue_head = response_to_queue; - } - else if(NULL == pos->next) - { - //put it at the end - response_to_queue->prev = pos; - pos->next = response_to_queue; - session->response_queue_tail = last; - } - else - { - response_to_queue->prev = pos; - last->next = pos->next; - pos->next = response_to_queue; - last->next->prev = last; - } -} - - -void -SPDYF_session_destroy(struct SPDY_Session *session) -{ - struct SPDYF_Stream *stream; - struct SPDYF_Response_Queue *response_queue; - - (void)MHD_socket_close_ (session->socket_fd); - SPDYF_zlib_deflate_end(&session->zlib_send_stream); - SPDYF_zlib_inflate_end(&session->zlib_recv_stream); - - //clean up unsent data in the output queue - while (NULL != (response_queue = session->response_queue_head)) - { - DLL_remove (session->response_queue_head, - session->response_queue_tail, - response_queue); - - if(NULL != response_queue->frqcb) - { - response_queue->frqcb(response_queue->frqcb_cls, response_queue, SPDY_RESPONSE_RESULT_SESSION_CLOSED); - } - - SPDYF_response_queue_destroy(response_queue); - } - - //clean up the streams belonging to this session - while (NULL != (stream = session->streams_head)) - { - DLL_remove (session->streams_head, - session->streams_tail, - stream); - - SPDYF_stream_destroy(stream); - } - - free(session->addr); - free(session->read_buffer); - free(session->write_buffer); - free(session); -} - - -int -SPDYF_prepare_goaway (struct SPDY_Session *session, - enum SPDY_GOAWAY_STATUS status, - bool in_front) -{ - struct SPDYF_Response_Queue *response_to_queue; - struct SPDYF_Control_Frame *control_frame; - uint32_t *data; - - if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue)))) - { - return SPDY_NO; - } - memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue)); - - if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame)))) - { - free(response_to_queue); - return SPDY_NO; - } - memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame)); - - if(NULL == (data = malloc(4))) - { - free(control_frame); - free(response_to_queue); - return SPDY_NO; - } - *(data) = htonl(status); - - control_frame->control_bit = 1; - control_frame->version = SPDY_VERSION; - control_frame->type = SPDY_CONTROL_FRAME_TYPES_GOAWAY; - control_frame->flags = 0; - - response_to_queue->control_frame = control_frame; - response_to_queue->process_response_handler = &SPDYF_handler_write_goaway; - response_to_queue->data = data; - response_to_queue->data_size = 4; - - SPDYF_queue_response (response_to_queue, - session, - in_front ? -1 : SPDY_NO); - - return SPDY_YES; -} - - -int -SPDYF_prepare_rst_stream (struct SPDY_Session *session, - struct SPDYF_Stream * stream, - enum SPDY_RST_STREAM_STATUS status) -{ - struct SPDYF_Response_Queue *response_to_queue; - struct SPDYF_Control_Frame *control_frame; - uint32_t *data; - uint32_t stream_id; - - if(NULL == stream) - stream_id = 0; - else - stream_id = stream->stream_id; - - if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue)))) - { - return SPDY_NO; - } - memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue)); - - if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame)))) - { - free(response_to_queue); - return SPDY_NO; - } - memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame)); - - if(NULL == (data = malloc(8))) - { - free(control_frame); - free(response_to_queue); - return SPDY_NO; - } - *(data) = HTON31(stream_id); - *(data + 1) = htonl(status); - - control_frame->control_bit = 1; - control_frame->version = SPDY_VERSION; - control_frame->type = SPDY_CONTROL_FRAME_TYPES_RST_STREAM; - control_frame->flags = 0; - - response_to_queue->control_frame = control_frame; - response_to_queue->process_response_handler = &SPDYF_handler_write_rst_stream; - response_to_queue->data = data; - response_to_queue->data_size = 8; - response_to_queue->stream = stream; - - SPDYF_queue_response (response_to_queue, - session, - -1); - - return SPDY_YES; -} - - -int -SPDYF_prepare_window_update (struct SPDY_Session *session, - struct SPDYF_Stream * stream, - int32_t delta_window_size) -{ - struct SPDYF_Response_Queue *response_to_queue; - struct SPDYF_Control_Frame *control_frame; - uint32_t *data; - - SPDYF_ASSERT(NULL != stream, "stream cannot be NULL"); - - if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue)))) - { - return SPDY_NO; - } - memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue)); - - if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame)))) - { - free(response_to_queue); - return SPDY_NO; - } - memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame)); - - if(NULL == (data = malloc(8))) - { - free(control_frame); - free(response_to_queue); - return SPDY_NO; - } - *(data) = HTON31(stream->stream_id); - *(data + 1) = HTON31(delta_window_size); - - control_frame->control_bit = 1; - control_frame->version = SPDY_VERSION; - control_frame->type = SPDY_CONTROL_FRAME_TYPES_WINDOW_UPDATE; - control_frame->flags = 0; - - response_to_queue->control_frame = control_frame; - response_to_queue->process_response_handler = &SPDYF_handler_write_window_update; - response_to_queue->data = data; - response_to_queue->data_size = 8; - response_to_queue->stream = stream; - - SPDYF_queue_response (response_to_queue, - session, - -1); - - return SPDY_YES; -} diff --git a/src/microspdy/session.h b/src/microspdy/session.h @@ -1,281 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file session.h - * @brief TCP connection/SPDY session handling - * @author Andrey Uzunov - */ - -#ifndef SESSION_H -#define SESSION_H - -#include "platform.h" -#include "structures.h" - -/** - * Called by the daemon when the socket for the session has available - * data to be read. Reads data from the TLS socket and puts it to the - * session's read buffer. The latte - * - * @param session SPDY_Session for which data will be read. - * @return SPDY_YES if something was read or session's status was - * changed. It is possible that error occurred but was handled - * and the status was therefore changed. - * SPDY_NO if nothing happened, e.g. the subsystem wants read/ - * write to be called again. - */ -int -SPDYF_session_read (struct SPDY_Session *session); - - -/** - * Called by the daemon when the socket for the session is ready for some - * data to be written to it. For one or more objects on the response - * queue tries to fill in the write buffer, based on the frame on the - * queue, and to write data to the TLS socket. - * - * @param session SPDY_Session for which data will be written. - * @param only_one_frame when true, the function will write at most one - * SPDY frame to the underlying IO subsystem; - * when false, the function will write up to - * session->max_num_frames SPDY frames - * @return SPDY_YES if the session's internal writing state has changed: - * something was written and/or session's status was - * changed and/or response callback was called but did not provide - * data. It is possible that error occurred but was handled - * and the status was therefore changed. - * SPDY_NO if nothing happened. However, it is possible that some - * frames were discarded within the call, e.g. frames belonging - * to a closed stream. - */ -int -SPDYF_session_write (struct SPDY_Session *session, - bool only_one_frame); - - -/** - * Called by the daemon on SPDY_run to handle the data in the read and write - * buffer of a session. Based on the state and the content of the read - * buffer new frames are received and interpreted, appropriate user - * callbacks are called and maybe something is put on the response queue - * ready to be handled by session_write. - * - * @param session SPDY_Session which will be handled. - * @return SPDY_YES if something from the read buffers was processed, - * session's status was changed and/or the session was closed. - * SPDY_NO if nothing happened, e.g. the session is in a state, - * not allowing processing read buffers. - */ -int -SPDYF_session_idle (struct SPDY_Session *session); - - -/** - * This function shutdowns the socket, moves the session structure to - * daemon's queue for sessions to be cleaned up. - * - * @param session SPDY_Session which will be handled. - */ -void -SPDYF_session_close (struct SPDY_Session *session); - - -/** - * Called to accept new TCP connection and create SPDY session. - * - * @param daemon SPDY_Daemon whose listening socket is used. - * @return SPDY_NO on any kind of error while accepting new TCP connection - * and initializing new SPDY_Session. - * SPDY_YES otherwise. - */ -int -SPDYF_session_accept(struct SPDY_Daemon *daemon); - - -/** - * Puts SPDYF_Response_Queue object on the queue to be sent to the - * client later. - * - * @param response_to_queue linked list of objects containing SPDY - * frame and data to be added to the queue - * @param session SPDY session for which the response is sent - * @param consider_priority if SPDY_NO, the list will be added to the - * end of the queue. - * If SPDY_YES, the response will be added after - * the last previously added response with priority of the - * request grater or equal to that of the current one. - * If -1, the object will be put at the head of the queue. - */ -void -SPDYF_queue_response (struct SPDYF_Response_Queue *response_to_queue, - struct SPDY_Session *session, - int consider_priority); - - -/** - * Cleans up the TSL context for the session, closes the TCP connection, - * cleans up any data pointed by members of the session structure - * (buffers, queue of responses, etc.) and frees the memory allocated by - * the session itself. - */ -void -SPDYF_session_destroy(struct SPDY_Session *session); - - -/** - * Prepares GOAWAY frame to tell the client to stop creating new streams. - * The session should be closed soon after this call. - * - * @param session SPDY session - * @param status code for the GOAWAY frame - * @param in_front whether or not to put the frame in front of everything - * on the response queue - * @return SPDY_NO on error (not enough memory) or - * SPDY_YES on success - */ -int -SPDYF_prepare_goaway (struct SPDY_Session *session, - enum SPDY_GOAWAY_STATUS status, - bool in_front); - - -/** - * Prepares RST_STREAM frame to terminate a stream. This frame may or - * not indicate an error. The frame will be put at the head of the queue. - * This means that frames for this stream which are still in the queue - * will be discarded soon. - * - * @param session SPDY session - * @param stream stream to terminate - * @param status code for the RST_STREAM frame - * @return SPDY_NO on memory error or - * SPDY_YES on success - */ -int -SPDYF_prepare_rst_stream (struct SPDY_Session *session, - struct SPDYF_Stream * stream, - enum SPDY_RST_STREAM_STATUS status); - - -/** - * Prepares WINDOW_UPDATE frame to tell the other party that more - * data can be sent on the stream. The frame will be put at the head of - * the queue. - * - * @param session SPDY session - * @param stream stream to which the changed window will apply - * @param delta_window_size how much the window grows - * @return SPDY_NO on memory error or - * SPDY_YES on success - */ -int -SPDYF_prepare_window_update (struct SPDY_Session *session, - struct SPDYF_Stream * stream, - int32_t delta_window_size); - - -/** - * Handler called by session_write to fill the write buffer according to - * the data frame waiting in the response queue. - * When response data is given by user callback, the lib does not know - * how many frames are needed. In such case this call produces - * another ResponseQueue object and puts it on the queue while the the - * user callback says that there will be more data. - * - * @return SPDY_NO on error (not enough memory or the user calback for - * providing response data did something wrong). If - * the error is unrecoverable the handler changes session's - * status. - * SPDY_YES on success - */ -int -SPDYF_handler_write_data (struct SPDY_Session *session); - - -/** - * Handler called by session_write to fill the write buffer based on the - * control frame (SYN_REPLY) waiting in the response queue. - * - * @param session SPDY session - * @return SPDY_NO on error (zlib state is broken; the session MUST be - * closed). If - * the error is unrecoverable the handler changes session's - * status. - * SPDY_YES on success - */ -int -SPDYF_handler_write_syn_reply (struct SPDY_Session *session); - - -/** - * Handler called by session_write to fill the write buffer based on the - * control frame (GOAWAY) waiting in the response queue. - * - * @param session SPDY session - * @return SPDY_NO on error (not enough memory; by specification the - * session must be closed - * soon, thus there is no need to handle the error) or - * SPDY_YES on success - */ -int -SPDYF_handler_write_goaway (struct SPDY_Session *session); - - -/** - * Handler called by session_write to fill the write buffer based on the - * control frame (RST_STREAM) waiting in the response queue. - * - * @param session SPDY session - * @return SPDY_NO on error (not enough memory). If - * the error is unrecoverable the handler changes session's - * status. - * SPDY_YES on success - */ -int -SPDYF_handler_write_rst_stream (struct SPDY_Session *session); - - -/** - * Handler called by session_write to fill the write buffer based on the - * control frame (WINDOW_UPDATE) waiting in the response queue. - * - * @param session SPDY session - * @return SPDY_NO on error (not enough memory). If - * the error is unrecoverable the handler changes session's - * status. - * SPDY_YES on success - */ -int -SPDYF_handler_write_window_update (struct SPDY_Session *session); - - -/** - * Carefully ignore the full size of frames which are not yet supported - * by the lib. - * TODO Ignoring frames containing compressed bodies means that the - * compress state will be corrupted on next received frame. According to - * the draft the lib SHOULD try to decompress data also in corrupted - * frames just to keep right compression state. - * - * @param session SPDY_Session whose read buffer is used. - */ -void -SPDYF_handler_ignore_frame (struct SPDY_Session *session); - -#endif diff --git a/src/microspdy/stream.c b/src/microspdy/stream.c @@ -1,169 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file stream.c - * @brief SPDY streams handling - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "structures.h" -#include "internal.h" -#include "session.h" - - -int -SPDYF_stream_new (struct SPDY_Session *session) -{ - uint32_t stream_id; - uint32_t assoc_stream_id; - uint8_t priority; - uint8_t slot; - size_t buffer_pos = session->read_buffer_beginning; - struct SPDYF_Stream *stream; - struct SPDYF_Control_Frame *frame; - - if((session->read_buffer_offset - session->read_buffer_beginning) < 10) - { - //not all fields are received to create new stream - return SPDY_NO; - } - - frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls; - - //get stream id of the new stream - memcpy(&stream_id, session->read_buffer + session->read_buffer_beginning, 4); - stream_id = NTOH31(stream_id); - session->read_buffer_beginning += 4; - if(stream_id <= session->last_in_stream_id || 0==(stream_id % 2)) - { - //wrong stream id sent by client - //GOAWAY with PROTOCOL_ERROR MUST be sent - //TODO - - //ignore frame - session->frame_handler = &SPDYF_handler_ignore_frame; - return SPDY_NO; - } - else if(session->is_goaway_sent) - { - //the client is not allowed to create new streams anymore - //we MUST ignore the frame - session->frame_handler = &SPDYF_handler_ignore_frame; - return SPDY_NO; - } - - //set highest stream id for session - session->last_in_stream_id = stream_id; - - //get assoc stream id of the new stream - //this value is used with SPDY PUSH, thus nothing to do with it here - memcpy(&assoc_stream_id, session->read_buffer + session->read_buffer_beginning, 4); - assoc_stream_id = NTOH31(assoc_stream_id); - session->read_buffer_beginning += 4; - - //get stream priority (3 bits) - //after it there are 5 bits that are not used - priority = *(uint8_t *)(session->read_buffer + session->read_buffer_beginning) >> 5; - session->read_buffer_beginning++; - - //get slot (see SPDY draft) - slot = *(uint8_t *)(session->read_buffer + session->read_buffer_beginning); - session->read_buffer_beginning++; - - if(NULL == (stream = malloc(sizeof(struct SPDYF_Stream)))) - { - SPDYF_DEBUG("No memory"); - //revert buffer state - session->read_buffer_beginning = buffer_pos; - return SPDY_NO; - } - memset(stream,0, sizeof(struct SPDYF_Stream)); - stream->session = session; - stream->stream_id = stream_id; - stream->assoc_stream_id = assoc_stream_id; - stream->priority = priority; - stream->slot = slot; - stream->is_in_closed = (frame->flags & SPDY_SYN_STREAM_FLAG_FIN) != 0; - stream->flag_unidirectional = (frame->flags & SPDY_SYN_STREAM_FLAG_UNIDIRECTIONAL) != 0; - stream->is_out_closed = stream->flag_unidirectional; - stream->is_server_initiator = false; - stream->window_size = SPDYF_INITIAL_WINDOW_SIZE; - - //put the stream to the list of streams for the session - DLL_insert(session->streams_head, session->streams_tail, stream); - - return SPDY_YES; -} - - -void -SPDYF_stream_destroy(struct SPDYF_Stream *stream) -{ - SPDY_name_value_destroy(stream->headers); - free(stream); - stream = NULL; -} - - -void -SPDYF_stream_set_flags_on_write(struct SPDYF_Response_Queue *response_queue) -{ - struct SPDYF_Stream * stream = response_queue->stream; - - if(NULL != response_queue->data_frame) - { - stream->is_out_closed = (bool)(response_queue->data_frame->flags & SPDY_DATA_FLAG_FIN); - } - else if(NULL != response_queue->control_frame) - { - switch(response_queue->control_frame->type) - { - case SPDY_CONTROL_FRAME_TYPES_SYN_REPLY: - stream->is_out_closed = (bool)(response_queue->control_frame->flags & SPDY_SYN_REPLY_FLAG_FIN); - break; - - case SPDY_CONTROL_FRAME_TYPES_RST_STREAM: - if(NULL != stream) - { - stream->is_out_closed = true; - stream->is_in_closed = true; - } - break; - - } - } -} - - -//TODO add function *on_read - - -struct SPDYF_Stream * -SPDYF_stream_find(uint32_t stream_id, struct SPDY_Session * session) -{ - struct SPDYF_Stream * stream = session->streams_head; - - while(NULL != stream && stream_id != stream->stream_id) - { - stream = stream->next; - } - - return stream; -} diff --git a/src/microspdy/stream.h b/src/microspdy/stream.h @@ -1,76 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file stream.h - * @brief SPDY streams handling - * @author Andrey Uzunov - */ - -#ifndef STREAM_H -#define STREAM_H - -#include "platform.h" - - -/** - * Reads data from session's read buffer and tries to create a new SPDY - * stream. This function is called after control frame's header has been - * read from the buffer (after the length field). If bogus frame is - * received the function changes the read handler of the session and - * fails, i.e. there is no need of further error handling by the caller. - * - * @param session SPDY_Session whose read buffer is being read - * @return SPDY_YES if a new SPDY stream request was correctly received - * and handled. SPDY_NO if the whole SPDY frame was not yet - * received or memory error occurred. - */ -int -SPDYF_stream_new (struct SPDY_Session *session); - - -/** - * Destroys stream structure and whatever is in it. - * - * @param stream SPDY_Stream to destroy - */ -void -SPDYF_stream_destroy(struct SPDYF_Stream *stream); - - -/** - * Set stream flags if needed based on the type of the frame that was - * just sent (e.g., close stream if it was RST_STREAM). - * - * @param response_queue sent for this stream - */ -void -SPDYF_stream_set_flags_on_write(struct SPDYF_Response_Queue *response_queue); - - -/** - * Find and return a session's stream, based on stream's ID. - * - * @param stream_id to search for - * @param session whose streams are considered - * @return SPDY_Stream with the desired ID. Can be NULL. - */ -struct SPDYF_Stream * -SPDYF_stream_find(uint32_t stream_id, struct SPDY_Session * session); - -#endif diff --git a/src/microspdy/structures.c b/src/microspdy/structures.c @@ -1,638 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file structures.c - * @brief Functions for handling most of the structures in defined - * in structures.h - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "structures.h" -#include "internal.h" -#include "session.h" -//TODO not for here? -#include <ctype.h> - - -int -SPDYF_name_value_is_empty(struct SPDY_NameValue *container) -{ - SPDYF_ASSERT(NULL != container, "NULL is not an empty container!"); - return (NULL == container->name && NULL == container->value) ? SPDY_YES : SPDY_NO; -} - -struct SPDY_NameValue * -SPDY_name_value_create () -{ - struct SPDY_NameValue *pair; - - if(NULL == (pair = malloc(sizeof(struct SPDY_NameValue)))) - return NULL; - - memset (pair, 0, sizeof (struct SPDY_NameValue)); - - return pair; -} - - -int -SPDY_name_value_add (struct SPDY_NameValue *container, - const char *name, - const char *value) -{ - unsigned int i; - unsigned int len; - struct SPDY_NameValue *pair; - struct SPDY_NameValue *temp; - char **temp_value; - char *temp_string; - - if(NULL == container || NULL == name || NULL == value || 0 == (len = strlen(name))) - return SPDY_INPUT_ERROR; - //TODO there is old code handling value==NULL - //update it to handle strlen(value)==0 - - for(i=0; i<len; ++i) - { - if(isupper((int) name[i])) - return SPDY_INPUT_ERROR; - } - - if(SPDYF_name_value_is_empty(container)) - { - //container is empty/just created - if (NULL == (container->name = strdup (name))) - { - return SPDY_NO; - } - if (NULL == (container->value = malloc(sizeof(char *)))) - { - free(container->name); - return SPDY_NO; - } - /*if(NULL == value) - container->value[0] = NULL; - else */if (NULL == (container->value[0] = strdup (value))) - { - free(container->value); - free(container->name); - return SPDY_NO; - } - container->num_values = 1; - return SPDY_YES; - } - - pair = container; - while(NULL != pair) - { - if(0 == strcmp(pair->name, name)) - { - //the value will be added to this pair - break; - } - pair = pair->next; - } - - if(NULL == pair) - { - //the name doesn't exist in container, add new pair - if(NULL == (pair = malloc(sizeof(struct SPDY_NameValue)))) - return SPDY_NO; - - memset(pair, 0, sizeof(struct SPDY_NameValue)); - - if (NULL == (pair->name = strdup (name))) - { - free(pair); - return SPDY_NO; - } - if (NULL == (pair->value = malloc(sizeof(char *)))) - { - free(pair->name); - free(pair); - return SPDY_NO; - } - /*if(NULL == value) - pair->value[0] = NULL; - else */if (NULL == (pair->value[0] = strdup (value))) - { - free(pair->value); - free(pair->name); - free(pair); - return SPDY_NO; - } - pair->num_values = 1; - - temp = container; - while(NULL != temp->next) - temp = temp->next; - temp->next = pair; - pair->prev = temp; - - return SPDY_YES; - } - - //check for duplication (case sensitive) - for(i=0; i<pair->num_values; ++i) - if(0 == strcmp(pair->value[i], value)) - return SPDY_NO; - - if(strlen(pair->value[0]) > 0) - { - //the value will be appended to the others for this name - if (NULL == (temp_value = malloc((pair->num_values + 1) * sizeof(char *)))) - { - return SPDY_NO; - } - memcpy(temp_value, pair->value, pair->num_values * sizeof(char *)); - if (NULL == (temp_value[pair->num_values] = strdup (value))) - { - free(temp_value); - return SPDY_NO; - } - free(pair->value); - pair->value = temp_value; - ++pair->num_values; - return SPDY_YES; - } - - //just replace the empty value - - if (NULL == (temp_string = strdup (value))) - { - return SPDY_NO; - } - free(pair->value[0]); - pair->value[0] = temp_string; - - return SPDY_YES; -} - - -const char * const * -SPDY_name_value_lookup (struct SPDY_NameValue *container, - const char *name, - int *num_values) -{ - struct SPDY_NameValue *temp = container; - - if(NULL == container || NULL == name || NULL == num_values) - return NULL; - if(SPDYF_name_value_is_empty(container)) - return NULL; - - do - { - if(strcmp(name, temp->name) == 0) - { - *num_values = temp->num_values; - return (const char * const *)temp->value; - } - - temp = temp->next; - } - while(NULL != temp); - - return NULL; -} - - -void -SPDY_name_value_destroy (struct SPDY_NameValue *container) -{ - unsigned int i; - struct SPDY_NameValue *temp = container; - - while(NULL != temp) - { - container = container->next; - free(temp->name); - for(i=0; i<temp->num_values; ++i) - free(temp->value[i]); - free(temp->value); - free(temp); - temp=container; - } -} - - -int -SPDY_name_value_iterate (struct SPDY_NameValue *container, - SPDY_NameValueIterator iterator, - void *iterator_cls) -{ - int count; - int ret; - struct SPDY_NameValue *temp = container; - - if(NULL == container) - return SPDY_INPUT_ERROR; - - //check if container is an empty struct - if(SPDYF_name_value_is_empty(container)) - return 0; - - count = 0; - - if(NULL == iterator) - { - do - { - ++count; - temp=temp->next; - } - while(NULL != temp); - - return count; - } - - //code duplication for avoiding if here - do - { - ++count; - ret = iterator(iterator_cls, temp->name, (const char * const *)temp->value, temp->num_values); - temp=temp->next; - } - while(NULL != temp && SPDY_YES == ret); - - return count; -} - -void -SPDY_destroy_response(struct SPDY_Response *response) -{ - if(NULL == response) - return; - free(response->data); - free(response->headers); - free(response); -} - - -struct SPDYF_Response_Queue * -SPDYF_response_queue_create(bool is_data, - void *data, - size_t data_size, - struct SPDY_Response *response, - struct SPDYF_Stream *stream, - bool closestream, - SPDYF_ResponseQueueResultCallback frqcb, - void *frqcb_cls, - SPDY_ResponseResultCallback rrcb, - void *rrcb_cls) -{ - struct SPDYF_Response_Queue *head = NULL; - struct SPDYF_Response_Queue *prev; - struct SPDYF_Response_Queue *response_to_queue; - struct SPDYF_Control_Frame *control_frame; - struct SPDYF_Data_Frame *data_frame; - unsigned int i; - bool is_last; - - SPDYF_ASSERT((! is_data) - || ((0 == data_size) && (NULL != response->rcb)) - || ((0 < data_size) && (NULL == response->rcb)), - "either data or request->rcb must not be null"); - - if (is_data && (data_size > SPDY_MAX_SUPPORTED_FRAME_SIZE)) - { - //separate the data in more frames and add them to the queue - - prev=NULL; - for(i = 0; i < data_size; i += SPDY_MAX_SUPPORTED_FRAME_SIZE) - { - is_last = (i + SPDY_MAX_SUPPORTED_FRAME_SIZE) >= data_size; - - if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue)))) - goto free_and_fail; - - memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue)); - if(0 == i) - head = response_to_queue; - - if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame)))) - { - free(response_to_queue); - goto free_and_fail; - } - memset(data_frame, 0, sizeof(struct SPDYF_Data_Frame)); - data_frame->control_bit = 0; - data_frame->stream_id = stream->stream_id; - if(is_last && closestream) - data_frame->flags |= SPDY_DATA_FLAG_FIN; - - response_to_queue->data_frame = data_frame; - response_to_queue->process_response_handler = &SPDYF_handler_write_data; - response_to_queue->is_data = is_data; - response_to_queue->stream = stream; - if(is_last) - { - response_to_queue->frqcb = frqcb; - response_to_queue->frqcb_cls = frqcb_cls; - response_to_queue->rrcb = rrcb; - response_to_queue->rrcb_cls = rrcb_cls; - } - response_to_queue->data = data + i; - response_to_queue->data_size = is_last - ? (data_size - 1) % SPDY_MAX_SUPPORTED_FRAME_SIZE + 1 - : SPDY_MAX_SUPPORTED_FRAME_SIZE; - response_to_queue->response = response; - - response_to_queue->prev = prev; - if(NULL != prev) - prev->next = response_to_queue; - prev = response_to_queue; - } - - return head; - - //for GOTO - free_and_fail: - while(NULL != head) - { - response_to_queue = head; - head = head->next; - free(response_to_queue->data_frame); - free(response_to_queue); - } - return NULL; - } - - //create only one frame for data, data with callback or control frame - - if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue)))) - { - return NULL; - } - memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue)); - - if(is_data) - { - if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame)))) - { - free(response_to_queue); - return NULL; - } - memset(data_frame, 0, sizeof(struct SPDYF_Data_Frame)); - data_frame->control_bit = 0; - data_frame->stream_id = stream->stream_id; - if(closestream && NULL == response->rcb) - data_frame->flags |= SPDY_DATA_FLAG_FIN; - - response_to_queue->data_frame = data_frame; - response_to_queue->process_response_handler = &SPDYF_handler_write_data; - } - else - { - if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame)))) - { - free(response_to_queue); - return NULL; - } - memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame)); - control_frame->control_bit = 1; - control_frame->version = SPDY_VERSION; - control_frame->type = SPDY_CONTROL_FRAME_TYPES_SYN_REPLY; - if(closestream) - control_frame->flags |= SPDY_SYN_REPLY_FLAG_FIN; - - response_to_queue->control_frame = control_frame; - response_to_queue->process_response_handler = &SPDYF_handler_write_syn_reply; - } - - response_to_queue->is_data = is_data; - response_to_queue->stream = stream; - response_to_queue->frqcb = frqcb; - response_to_queue->frqcb_cls = frqcb_cls; - response_to_queue->rrcb = rrcb; - response_to_queue->rrcb_cls = rrcb_cls; - response_to_queue->data = data; - response_to_queue->data_size = data_size; - response_to_queue->response = response; - - return response_to_queue; -} - - -void -SPDYF_response_queue_destroy(struct SPDYF_Response_Queue *response_queue) -{ - //data is not copied to the struct but only linked - //but this is not valid for GOAWAY and RST_STREAM - if(!response_queue->is_data - && (SPDY_CONTROL_FRAME_TYPES_RST_STREAM == response_queue->control_frame->type - || SPDY_CONTROL_FRAME_TYPES_GOAWAY == response_queue->control_frame->type)) - { - free(response_queue->data); - } - if(response_queue->is_data) - free(response_queue->data_frame); - else - free(response_queue->control_frame); - - free(response_queue); -} - - -/* Needed by testcase to be extern -- should this be - in the header? */ -_MHD_EXTERN ssize_t -SPDYF_name_value_to_stream(struct SPDY_NameValue * container[], - int num_containers, - void **stream) -{ - size_t size; - int32_t num_pairs = 0; - int32_t value_size; - int32_t name_size; - int32_t temp; - unsigned int i; - unsigned int offset; - unsigned int value_offset; - struct SPDY_NameValue * iterator; - int j; - - size = 4; //for num pairs - - for(j=0; j<num_containers; ++j) - { - iterator = container[j]; - while(iterator != NULL) - { - ++num_pairs; - size += 4 + strlen(iterator->name); //length + string - - SPDYF_ASSERT(iterator->num_values>0, "num_values is 0"); - - size += 4; //value length - - for(i=0; i<iterator->num_values; ++i) - { - //if(NULL == iterator->value[i]) - // continue; - size += strlen(iterator->value[i]); // string - if(i/* || !strlen(iterator->value[i])*/) ++size; //NULL separator - } - - iterator = iterator->next; - } - } - - if(NULL == (*stream = malloc(size))) - { - return -1; - } - - //put num_pairs to the stream - num_pairs = htonl(num_pairs); - memcpy(*stream, &num_pairs, 4); - offset = 4; - - //put all other headers to the stream - for(j=0; j<num_containers; ++j) - { - iterator = container[j]; - while(iterator != NULL) - { - name_size = strlen(iterator->name); - temp = htonl(name_size); - memcpy(*stream + offset, &temp, 4); - offset += 4; - strncpy(*stream + offset, iterator->name, name_size); - offset += name_size; - - value_offset = offset; - offset += 4; - for(i=0; i<iterator->num_values; ++i) - { - if(i /*|| !strlen(iterator->value[0])*/) - { - memset(*stream + offset, 0, 1); - ++offset; - //if(!i) continue; - } - //else if(NULL != iterator->value[i]) - //{ - strncpy(*stream + offset, iterator->value[i], strlen(iterator->value[i])); - offset += strlen(iterator->value[i]); - //} - } - value_size = offset - value_offset - 4; - value_size = htonl(value_size); - memcpy(*stream + value_offset, &value_size, 4); - - iterator = iterator->next; - } - } - - SPDYF_ASSERT(offset == size,"offset is wrong"); - - return size; -} - - -/* Needed by testcase to be extern -- should this be - in the header? */ -_MHD_EXTERN int -SPDYF_name_value_from_stream(void *stream, - size_t size, - struct SPDY_NameValue ** container) -{ - int32_t num_pairs; - int32_t value_size; - int32_t name_size; - int i; - unsigned int offset = 0; - unsigned int value_end_offset; - char *name; - char *value; - - if(NULL == (*container = SPDY_name_value_create ())) - { - return SPDY_NO; - } - - //get number of pairs - memcpy(&num_pairs, stream, 4); - offset = 4; - num_pairs = ntohl(num_pairs); - - if(num_pairs > 0) - { - for(i = 0; i < num_pairs; ++i) - { - //get name size - memcpy(&name_size, stream + offset, 4); - offset += 4; - name_size = ntohl(name_size); - //get name - if(NULL == (name = strndup(stream + offset, name_size))) - { - SPDY_name_value_destroy(*container); - return SPDY_NO; - } - offset+=name_size; - - //get value size - memcpy(&value_size, stream + offset, 4); - offset += 4; - value_size = ntohl(value_size); - value_end_offset = offset + value_size; - //get value - do - { - if(NULL == (value = strndup(stream + offset, value_size))) - { - free(name); - SPDY_name_value_destroy(*container); - return SPDY_NO; - } - offset += strlen(value); - if(offset < value_end_offset) - ++offset; //NULL separator - - //add name/value to the struct - if(SPDY_YES != SPDY_name_value_add(*container, name, value)) - { - free(name); - free(value); - SPDY_name_value_destroy(*container); - return SPDY_NO; - } - free(value); - } - while(offset < value_end_offset); - - free(name); - - if(offset != value_end_offset) - { - SPDY_name_value_destroy(*container); - return SPDY_INPUT_ERROR; - } - } - } - - if(offset == size) - return SPDY_YES; - - SPDY_name_value_destroy(*container); - return SPDY_INPUT_ERROR; -} diff --git a/src/microspdy/structures.h b/src/microspdy/structures.h @@ -1,1246 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file structures.h - * @brief internal and public structures -- most of the structs used by - * the library are defined here - * @author Andrey Uzunov - */ - -#ifndef STRUCTURES_H -#define STRUCTURES_H - -#include "platform.h" -#include "microspdy.h" -#include "io.h" - - -/** - * All possible SPDY control frame types. The number is used in the header - * of the control frame. - */ -enum SPDY_CONTROL_FRAME_TYPES -{ - /** - * The SYN_STREAM control frame allows the sender to asynchronously - * create a stream between the endpoints. - */ - SPDY_CONTROL_FRAME_TYPES_SYN_STREAM = 1, - - /** - * SYN_REPLY indicates the acceptance of a stream creation by - * the recipient of a SYN_STREAM frame. - */ - SPDY_CONTROL_FRAME_TYPES_SYN_REPLY = 2, - - /** - * The RST_STREAM frame allows for abnormal termination of a stream. - * When sent by the creator of a stream, it indicates the creator - * wishes to cancel the stream. When sent by the recipient of a - * stream, it indicates an error or that the recipient did not want - * to accept the stream, so the stream should be closed. - */ - SPDY_CONTROL_FRAME_TYPES_RST_STREAM = 3, - - /** - * A SETTINGS frame contains a set of id/value pairs for - * communicating configuration data about how the two endpoints may - * communicate. SETTINGS frames can be sent at any time by either - * endpoint, are optionally sent, and are fully asynchronous. When - * the server is the sender, the sender can request that - * configuration data be persisted by the client across SPDY - * sessions and returned to the server in future communications. - */ - SPDY_CONTROL_FRAME_TYPES_SETTINGS = 4, - - /** - * The PING control frame is a mechanism for measuring a minimal - * round-trip time from the sender. It can be sent from the client - * or the server. Recipients of a PING frame should send an - * identical frame to the sender as soon as possible (if there is - * other pending data waiting to be sent, PING should take highest - * priority). Each ping sent by a sender should use a unique ID. - */ - SPDY_CONTROL_FRAME_TYPES_PING = 6, - - /** - * The GOAWAY control frame is a mechanism to tell the remote side - * of the connection to stop creating streams on this session. It - * can be sent from the client or the server. - */ - SPDY_CONTROL_FRAME_TYPES_GOAWAY = 7, - - /** - * The HEADERS frame augments a stream with additional headers. It - * may be optionally sent on an existing stream at any time. - * Specific application of the headers in this frame is - * application-dependent. The name/value header block within this - * frame is compressed. - */ - SPDY_CONTROL_FRAME_TYPES_HEADERS = 8, - - /** - * The WINDOW_UPDATE control frame is used to implement per stream - * flow control in SPDY. Flow control in SPDY is per hop, that is, - * only between the two endpoints of a SPDY connection. If there are - * one or more intermediaries between the client and the origin - * server, flow control signals are not explicitly forwarded by the - * intermediaries. - */ - SPDY_CONTROL_FRAME_TYPES_WINDOW_UPDATE = 9, - - /** - * The CREDENTIAL control frame is used by the client to send - * additional client certificates to the server. A SPDY client may - * decide to send requests for resources from different origins on - * the same SPDY session if it decides that that server handles both - * origins. For example if the IP address associated with both - * hostnames matches and the SSL server certificate presented in the - * initial handshake is valid for both hostnames. However, because - * the SSL connection can contain at most one client certificate, - * the client needs a mechanism to send additional client - * certificates to the server. - */ - SPDY_CONTROL_FRAME_TYPES_CREDENTIAL = 11 -}; - - -/** - * SPDY_SESSION_STATUS is used to show the current receiving state - * of each session, i.e. what is expected to come now, and how it should - * be handled. - */ -enum SPDY_SESSION_STATUS -{ - /** - * The session is in closing state, do not read read anything from - * it. Do not write anything to it. - */ - SPDY_SESSION_STATUS_CLOSING = 0, - - /** - * Wait for new SPDY frame to come. - */ - SPDY_SESSION_STATUS_WAIT_FOR_HEADER = 1, - - /** - * The standard 8 byte header of the SPDY frame was received and - * handled. Wait for the specific (sub)headers according to the - * frame type. - */ - SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER = 2, - - /** - * The specific (sub)headers were received and handled. Wait for the - * "body", i.e. wait for the name/value pairs compressed by zlib. - */ - SPDY_SESSION_STATUS_WAIT_FOR_BODY = 3, - - /** - * Ignore all the bytes read from the socket, e.g. larger frames. - */ - SPDY_SESSION_STATUS_IGNORE_BYTES= 4, - - /** - * The session is in pre-closing state, do not read read anything - * from it. In this state the output queue will be written to the - * socket. - */ - SPDY_SESSION_STATUS_FLUSHING = 5, -}; - - -/** - * Specific flags for the SYN_STREAM control frame. - */ -enum SPDY_SYN_STREAM_FLAG -{ - /** - * The sender won't send any more frames on this stream. - */ - SPDY_SYN_STREAM_FLAG_FIN = 1, - - /** - * The sender creates this stream as unidirectional. - */ - SPDY_SYN_STREAM_FLAG_UNIDIRECTIONAL = 2 -}; - - -/** - * Specific flags for the SYN_REPLY control frame. - */ -enum SPDY_SYN_REPLY_FLAG -{ - /** - * The sender won't send any more frames on this stream. - */ - SPDY_SYN_REPLY_FLAG_FIN = 1 -}; - - -/** - * Specific flags for the data frame. - */ -enum SPDY_DATA_FLAG -{ - /** - * The sender won't send any more frames on this stream. - */ - SPDY_DATA_FLAG_FIN = 1, - - /** - * The data in the frame is compressed. - * This flag appears only in the draft on ietf.org but not on - * chromium.org. - */ - SPDY_DATA_FLAG_COMPRESS = 2 -}; - -/** - * Status code within RST_STREAM control frame. - */ -enum SPDY_RST_STREAM_STATUS -{ - /** - * This is a generic error, and should only be used if a more - * specific error is not available. - */ - SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR = 1, - - /** - * This is returned when a frame is received for a stream which is - * not active. - */ - SPDY_RST_STREAM_STATUS_INVALID_STREAM = 2, - - /** - * Indicates that the stream was refused before any processing has - * been done on the stream. - */ - SPDY_RST_STREAM_STATUS_REFUSED_STREAM = 3, - - /** - * Indicates that the recipient of a stream does not support the - * SPDY version requested. - */ - SPDY_RST_STREAM_STATUS_UNSUPPORTED_VERSION = 4, - - /** - * Used by the creator of a stream to indicate that the stream is - * no longer needed. - */ - SPDY_RST_STREAM_STATUS_CANCEL = 5, - - /** - * This is a generic error which can be used when the implementation - * has internally failed, not due to anything in the protocol. - */ - SPDY_RST_STREAM_STATUS_INTERNAL_ERROR = 6, - - /** - * The endpoint detected that its peer violated the flow control - * protocol. - */ - SPDY_RST_STREAM_STATUS_FLOW_CONTROL_ERROR = 7, - - /** - * The endpoint received a SYN_REPLY for a stream already open. - */ - SPDY_RST_STREAM_STATUS_STREAM_IN_USE = 8, - - /** - * The endpoint received a data or SYN_REPLY frame for a stream - * which is half closed. - */ - SPDY_RST_STREAM_STATUS_STREAM_ALREADY_CLOSED = 9, - - /** - * The server received a request for a resource whose origin does - * not have valid credentials in the client certificate vector. - */ - SPDY_RST_STREAM_STATUS_INVALID_CREDENTIALS = 10, - - /** - * The endpoint received a frame which this implementation could not - * support. If FRAME_TOO_LARGE is sent for a SYN_STREAM, HEADERS, - * or SYN_REPLY frame without fully processing the compressed - * portion of those frames, then the compression state will be - * out-of-sync with the other endpoint. In this case, senders of - * FRAME_TOO_LARGE MUST close the session. - */ - SPDY_RST_STREAM_STATUS_FRAME_TOO_LARGE = 11 -}; - - -/** - * Status code within GOAWAY control frame. - */ -enum SPDY_GOAWAY_STATUS -{ - /** - * This is a normal session teardown. - */ - SPDY_GOAWAY_STATUS_OK = 0, - - /** - * This is a generic error, and should only be used if a more - * specific error is not available. - */ - SPDY_GOAWAY_STATUS_PROTOCOL_ERROR = 1, - - /** - * This is a generic error which can be used when the implementation - * has internally failed, not due to anything in the protocol. - */ - SPDY_GOAWAY_STATUS_INTERNAL_ERROR = 11 -}; - - -struct SPDYF_Stream; - -struct SPDYF_Response_Queue; - - -/** - * Callback for received new data chunk. - * - * @param cls client-defined closure - * @param stream handler - * @param buf data chunk from the data - * @param size the size of the data chunk 'buf' in bytes - * @param more false if this is the last frame received on this stream. Note: - * true does not mean that more data will come, exceptional - * situation is possible - * @return SPDY_YES to continue calling the function, - * SPDY_NO to stop calling the function for this stream - */ -typedef int -(*SPDYF_NewDataCallback) (void * cls, - struct SPDYF_Stream *stream, - const void * buf, - size_t size, - bool more); - - -/** - * Callback for new stream. To be used in the application layer of the - * lib. - * - * @param cls - * @param stream the new stream - * @return SPDY_YES on success, - * SPDY_NO if error occurs - */ -typedef int -(*SPDYF_NewStreamCallback) (void *cls, - struct SPDYF_Stream * stream); - - -/** - * Callback to be called when the response queue object was handled and - * the data was already sent. - * - * @param cls - * @param response_queue the SPDYF_Response_Queue structure which will - * be cleaned very soon - * @param status shows if actually the response was sent or it was - * discarded by the lib for any reason (e.g., closing session, - * closing stream, stopping daemon, etc.). It is possible that - * status indicates an error but part of the response (in one - * or several frames) was sent to the client. - */ -typedef void -(*SPDYF_ResponseQueueResultCallback) (void * cls, - struct SPDYF_Response_Queue *response_queue, - enum SPDY_RESPONSE_RESULT status); - - -/** - * Representation of the control frame's headers, which are common for - * all types. - */ -struct __attribute__((__packed__)) SPDYF_Control_Frame -{ - uint16_t version : 15; - uint16_t control_bit : 1; /* always 1 for control frames */ - uint16_t type; - uint32_t flags : 8; - uint32_t length : 24; -}; - - -/** - * Representation of the data frame's headers. - */ -struct __attribute__((__packed__)) SPDYF_Data_Frame -{ - uint32_t stream_id : 31; - uint32_t control_bit : 1; /* always 0 for data frames */ - uint32_t flags : 8; - uint32_t length : 24; -}; - - -/** - * Queue of the responses, to be handled (e.g. compressed) and sent later. - */ -struct SPDYF_Response_Queue -{ - /** - * This is a doubly-linked list. - */ - struct SPDYF_Response_Queue *next; - - /** - * This is a doubly-linked list. - */ - struct SPDYF_Response_Queue *prev; - - /** - * Stream (Request) for which is the response. - */ - struct SPDYF_Stream *stream; - - /** - * Response structure with all the data (uncompressed headers) to be sent. - */ - struct SPDY_Response *response; - - /** - * Control frame. The length field should be set after compressing - * the headers! - */ - struct SPDYF_Control_Frame *control_frame; - - /** - * Data frame. The length field should be set after compressing - * the body! - */ - struct SPDYF_Data_Frame *data_frame; - - /** - * Data to be sent: name/value pairs in control frames or body in data frames. - */ - void *data; - - /** - * Specific handler for different frame types. - */ - int (* process_response_handler)(struct SPDY_Session *session); - - /** - * Callback to be called when the last bytes from the response was sent - * to the client. - */ - SPDYF_ResponseQueueResultCallback frqcb; - - /** - * Closure for frqcb. - */ - void *frqcb_cls; - - /** - * Callback to be used by the application layer. - */ - SPDY_ResponseResultCallback rrcb; - - /** - * Closure for rcb. - */ - void *rrcb_cls; - - /** - * Data size. - */ - size_t data_size; - - /** - * True if data frame should be sent. False if control frame should - * be sent. - */ - bool is_data; -}; - - - -/** - * Collection of HTTP headers used in requests and responses. - */ -struct SPDY_NameValue -{ - /** - * This is a doubly-linked list. - */ - struct SPDY_NameValue *next; - - /** - * This is a doubly-linked list. - */ - struct SPDY_NameValue *prev; - - /** - * Null terminated string for name. - */ - char *name; - - /** - * Array of Null terminated strings for value. num_values is the - * length of the array. - */ - char **value; - - /** - * Number of values, this is >= 0. - */ - unsigned int num_values; -}; - - -/** - * Represents a SPDY stream - */ -struct SPDYF_Stream -{ - /** - * This is a doubly-linked list. - */ - struct SPDYF_Stream *next; - - /** - * This is a doubly-linked list. - */ - struct SPDYF_Stream *prev; - - /** - * Reference to the SPDY_Session struct. - */ - struct SPDY_Session *session; - - /** - * Name value pairs, sent within the frame which created the stream. - */ - struct SPDY_NameValue *headers; - - /** - * Any object to be used by the application layer. - */ - void *cls; - - /** - * This stream's ID. - */ - uint32_t stream_id; - - /** - * Stream to which this one is associated. - */ - uint32_t assoc_stream_id; - - /** - * The window of the data within data frames. - */ - uint32_t window_size; - - /** - * Stream priority. 0 is the highest, 7 is the lowest. - */ - uint8_t priority; - - /** - * Integer specifying the index in the server's CREDENTIAL vector of - * the client certificate to be used for this request The value 0 - * means no client certificate should be associated with this stream. - */ - uint8_t slot; - - /** - * If initially the stream was created as unidirectional. - */ - bool flag_unidirectional; - - /** - * If the stream won't be used for receiving frames anymore. The - * client has sent FLAG_FIN or the stream was terminated with - * RST_STREAM. - */ - bool is_in_closed; - - /** - * If the stream won't be used for sending out frames anymore. The - * server has sent FLAG_FIN or the stream was terminated with - * RST_STREAM. - */ - bool is_out_closed; - - /** - * Which entity (server/client) has created the stream. - */ - bool is_server_initiator; -}; - - -/** - * Represents a SPDY session which is just a TCP connection - */ -struct SPDY_Session -{ - /** - * zlib stream for decompressing all the name/pair values from the - * received frames. All the received compressed data must be - * decompressed within one context: this stream. Thus, it should be - * unique for the session and initialized at its creation. - */ - z_stream zlib_recv_stream; - - /** - * zlib stream for compressing all the name/pair values from the - * frames to be sent. All the sent compressed data must be - * compressed within one context: this stream. Thus, it should be - * unique for the session and initialized at its creation. - */ - z_stream zlib_send_stream; - - /** - * This is a doubly-linked list. - */ - struct SPDY_Session *next; - - /** - * This is a doubly-linked list. - */ - struct SPDY_Session *prev; - - /** - * Reference to the SPDY_Daemon struct. - */ - struct SPDY_Daemon *daemon; - - /** - * Foreign address (of length addr_len). - */ - struct sockaddr *addr; - - /** - * Head of doubly-linked list of the SPDY streams belonging to the - * session. - */ - struct SPDYF_Stream *streams_head; - - /** - * Tail of doubly-linked list of the streams. - */ - struct SPDYF_Stream *streams_tail; - - /** - * Unique IO context for the session. Initialized on each creation - * (actually when the TCP connection is established). - */ - void *io_context; - - /** - * Head of doubly-linked list of the responses. - */ - struct SPDYF_Response_Queue *response_queue_head; - - /** - * Tail of doubly-linked list of the responses. - */ - struct SPDYF_Response_Queue *response_queue_tail; - - /** - * Buffer for reading requests. - */ - void *read_buffer; - - /** - * Buffer for writing responses. - */ - void *write_buffer; - - /** - * Specific handler for the frame that is currently being received. - */ - void (*frame_handler) (struct SPDY_Session * session); - - /** - * Closure for frame_handler. - */ - void *frame_handler_cls; - - /** - * Extra field to be used by the user with set/get func for whatever - * purpose he wants. - */ - void *user_cls; - - /** - * Function to initialize the IO context for a new session. - */ - SPDYF_IONewSession fio_new_session; - - /** - * Function to deinitialize the IO context for a session. - */ - SPDYF_IOCloseSession fio_close_session; - - /** - * Function to read data from socket. - */ - SPDYF_IORecv fio_recv; - - /** - * Function to write data to socket. - */ - SPDYF_IOSend fio_send; - - /** - * Function to check for pending data in IO buffers. - */ - SPDYF_IOIsPending fio_is_pending; - - /** - * Function to call before writing set of frames. - */ - SPDYF_IOBeforeWrite fio_before_write; - - /** - * Function to call after writing set of frames. - */ - SPDYF_IOAfterWrite fio_after_write; - - /** - * Number of bytes that the lib must ignore immediately after they - * are read from the TLS socket without adding them to the read buf. - * This is needed, for instance, when receiving frame bigger than - * the buffer to avoid deadlock situations. - */ - size_t read_ignore_bytes; - - /** - * Size of read_buffer (in bytes). This value indicates - * how many bytes we're willing to read into the buffer; - * the real buffer is one byte longer to allow for - * adding zero-termination (when needed). - */ - size_t read_buffer_size; - - /** - * Position where we currently append data in - * read_buffer (last valid position). - */ - size_t read_buffer_offset; - - /** - * Position until where everything was already read - */ - size_t read_buffer_beginning; - - /** - * Size of write_buffer (in bytes). This value indicates - * how many bytes we're willing to prepare for writing. - */ - size_t write_buffer_size; - - /** - * Position where we currently append data in - * write_buffer (last valid position). - */ - size_t write_buffer_offset; - - /** - * Position until where everything was already written to the socket - */ - size_t write_buffer_beginning; - - /** - * Last time this connection had any activity - * (reading or writing). In milliseconds. - */ - unsigned long long last_activity; - - /** - * Socket for this connection. Set to -1 if - * this connection has died (daemon should clean - * up in that case). - */ - int socket_fd; - - /** - * Length of the foreign address. - */ - socklen_t addr_len; - - /** - * The biggest stream ID for this session for streams initiated - * by the client. - */ - uint32_t last_in_stream_id; - - /** - * The biggest stream ID for this session for streams initiated - * by the server. - */ - uint32_t last_out_stream_id; - - /** - * This value is updated whenever SYN_REPLY or RST_STREAM are sent - * and is used later in GOAWAY frame. - * TODO it is not clear in the draft what happens when streams are - * not answered in the order of their IDs. Moreover, why should we - * send GOAWAY with the ID of received bogus SYN_STREAM with huge ID? - */ - uint32_t last_replied_to_stream_id; - - /** - * Shows the stream id of the currently handled frame. This value is - * to be used when sending RST_STREAM in answer to a problematic - * frame, e.g. larger than supported. - */ - uint32_t current_stream_id; - - /** - * Maximum number of frames to be written to the socket at once. The - * library tries to send max_num_frames in a single call to SPDY_run - * for a single session. This means no requests can be received nor - * other sessions can send data as long the current one has enough - * frames to send and there is no error on writing. - */ - uint32_t max_num_frames; - - /** - * Shows the current receiving state the session, i.e. what is - * expected to come now, and how it shold be handled. - */ - enum SPDY_SESSION_STATUS status; - - /** - * Has this socket been closed for reading (i.e. - * other side closed the connection)? If so, - * we must completely close the connection once - * we are done sending our response (and stop - * trying to read from this socket). - */ - bool read_closed; - - /** - * If the server sends GOAWAY, it must ignore all SYN_STREAMS for - * this session. Normally the server will soon close the TCP session. - */ - bool is_goaway_sent; - - /** - * If the server receives GOAWAY, it must not send new SYN_STREAMS - * on this session. Normally the client will soon close the TCP - * session. - */ - bool is_goaway_received; -}; - - -/** - * State and settings kept for each SPDY daemon. - */ -struct SPDY_Daemon -{ - - /** - * Tail of doubly-linked list of our current, active sessions. - */ - struct SPDY_Session *sessions_head; - - /** - * Tail of doubly-linked list of our current, active sessions. - */ - struct SPDY_Session *sessions_tail; - - /** - * Tail of doubly-linked list of connections to clean up. - */ - struct SPDY_Session *cleanup_head; - - /** - * Tail of doubly-linked list of connections to clean up. - */ - struct SPDY_Session *cleanup_tail; - - /** - * Unique IO context for the daemon. Initialized on daemon start. - */ - void *io_context; - - /** - * Certificate file of the server. File path is kept here. - */ - char *certfile; - - /** - * Key file for the certificate of the server. File path is - * kept here. - */ - char *keyfile; - - - /** - * The address to which the listening socket is bound. - */ - struct sockaddr *address; - - /** - * Callback called when a new SPDY session is - * established by a client - */ - SPDY_NewSessionCallback new_session_cb; - - /** - * Callback called when a client closes the session - */ - SPDY_SessionClosedCallback session_closed_cb; - - /** - * Callback called when a client sends request - */ - SPDY_NewRequestCallback new_request_cb; - - /** - * Callback called when HTTP POST params are received - * after request. To be used by the application layer - */ - SPDY_NewDataCallback received_data_cb; - - /** - * Callback called when DATA frame is received. - */ - SPDYF_NewDataCallback freceived_data_cb; - - /** - * Closure argument for all the callbacks that can be used by the client. - */ - void *cls; - - /** - * Callback called when new stream is created. - */ - SPDYF_NewStreamCallback fnew_stream_cb; - - /** - * Closure argument for all the callbacks defined in the framing layer. - */ - void *fcls; - - /** - * Function to initialize the IO context for the daemon. - */ - SPDYF_IOInit fio_init; - - /** - * Function to deinitialize the IO context for the daemon. - */ - SPDYF_IODeinit fio_deinit; - - /** - * After how many milliseconds of inactivity should - * connections time out? Zero for no timeout. - */ - unsigned long long session_timeout; - - /** - * Listen socket. - */ - int socket_fd; - - /** - * This value is inherited by all sessions of the daemon. - * Maximum number of frames to be written to the socket at once. The - * library tries to send max_num_frames in a single call to SPDY_run - * for a single session. This means no requests can be received nor - * other sessions can send data as long the current one has enough - * frames to send and there is no error on writing. - */ - uint32_t max_num_frames; - - /** - * Daemon's options. - */ - enum SPDY_DAEMON_OPTION options; - - /** - * Daemon's flags. - */ - enum SPDY_DAEMON_FLAG flags; - - /** - * IO subsystem type used by daemon and all its sessions. - */ - enum SPDY_IO_SUBSYSTEM io_subsystem; - - /** - * Listen port. - */ - uint16_t port; -}; - - -/** - * Represents a SPDY response. - */ -struct SPDY_Response -{ - /** - * Raw uncompressed stream of the name/value pairs in SPDY frame - * used for the HTTP headers. - */ - void *headers; - - /** - * Raw stream of the data to be sent. Equivalent to the body in HTTP - * response. - */ - void *data; - - /** - * Callback function to be used when the response data is provided - * with callbacks. In this case data must be NULL and data_size must - * be 0. - */ - SPDY_ResponseCallback rcb; - - /** - * Extra argument to rcb. - */ - void *rcb_cls; - - /** - * Length of headers. - */ - size_t headers_size; - - /** - * Length of data. - */ - size_t data_size; - - /** - * The callback func will be called to get that amount of bytes to - * put them into a DATA frame. It is either user preffered or - * the maximum supported by the lib value. - */ - uint32_t rcb_block_size; -}; - - -/* Macros for handling data and structures */ - - -/** - * Insert an element at the head of a DLL. Assumes that head, tail and - * element are structs with prev and next fields. - * - * @param head pointer to the head of the DLL (struct ? *) - * @param tail pointer to the tail of the DLL (struct ? *) - * @param element element to insert (struct ? *) - */ -#define DLL_insert(head,tail,element) do { \ - (element)->next = (head); \ - (element)->prev = NULL; \ - if ((tail) == NULL) \ - (tail) = element; \ - else \ - (head)->prev = element; \ - (head) = (element); } while (0) - - -/** - * Remove an element from a DLL. Assumes - * that head, tail and element are structs - * with prev and next fields. - * - * @param head pointer to the head of the DLL (struct ? *) - * @param tail pointer to the tail of the DLL (struct ? *) - * @param element element to remove (struct ? *) - */ -#define DLL_remove(head,tail,element) do { \ - if ((element)->prev == NULL) \ - (head) = (element)->next; \ - else \ - (element)->prev->next = (element)->next; \ - if ((element)->next == NULL) \ - (tail) = (element)->prev; \ - else \ - (element)->next->prev = (element)->prev; \ - (element)->next = NULL; \ - (element)->prev = NULL; } while (0) - - -/** - * Convert all integers in a SPDY control frame headers structure from - * host byte order to network byte order. - * - * @param frame input and output structure (struct SPDY_Control_Frame *) - */ -#if HAVE_BIG_ENDIAN -#define SPDYF_CONTROL_FRAME_HTON(frame) -#else -#define SPDYF_CONTROL_FRAME_HTON(frame) do { \ - (*((uint16_t *) frame )) = (*((uint8_t *) (frame) +1 )) | ((*((uint8_t *) frame ))<<8);\ - (frame)->type = htons((frame)->type); \ - (frame)->length = HTON24((frame)->length); \ - } while (0) -#endif - - -/** - * Convert all integers in a SPDY control frame headers structure from - * network byte order to host byte order. - * - * @param frame input and output structure (struct SPDY_Control_Frame *) - */ -#if HAVE_BIG_ENDIAN -#define SPDYF_CONTROL_FRAME_NTOH(frame) -#else -#define SPDYF_CONTROL_FRAME_NTOH(frame) do { \ - (*((uint16_t *) frame )) = (*((uint8_t *) (frame) +1 )) | ((*((uint8_t *) frame ))<<8);\ - (frame)->type = ntohs((frame)->type); \ - (frame)->length = NTOH24((frame)->length); \ - } while (0) -#endif - - -/** - * Convert all integers in a SPDY data frame headers structure from - * host byte order to network byte order. - * - * @param frame input and output structure (struct SPDY_Data_Frame *) - */ -#if HAVE_BIG_ENDIAN -#define SPDYF_DATA_FRAME_HTON(frame) -#else -#define SPDYF_DATA_FRAME_HTON(frame) do { \ - *((uint32_t *) frame ) = htonl(*((uint32_t *) frame ));\ - (frame)->length = HTON24((frame)->length); \ - } while (0) -#endif - - -/** - * Convert all integers in a SPDY data frame headers structure from - * network byte order to host byte order. - * - * @param frame input and output structure (struct SPDY_Data_Frame *) - */ -#if HAVE_BIG_ENDIAN -#define SPDYF_DATA_FRAME_NTOH(frame) -#else -#define SPDYF_DATA_FRAME_NTOH(frame) do { \ - *((uint32_t *) frame ) = ntohl(*((uint32_t *) frame ));\ - (frame)->length = NTOH24((frame)->length); \ - } while (0) -#endif - - -/** - * Creates one or more new SPDYF_Response_Queue object to be put on the - * response queue. - * - * @param is_data whether new data frame or new control frame will be - * crerated - * @param data the row stream which will be used as the body of the frame - * @param data_size length of data - * @param response object, part of which is the frame - * @param stream on which data is to be sent - * @param closestream TRUE if the frame must close the stream (with flag) - * @param frqcb callback to notify application layer when the frame - * has been sent or discarded - * @param frqcb_cls closure for frqcb - * @param rrcb callback used by the application layer to notify the - * application when the frame has been sent or discarded. - * frqcb will call it - * @param rrcb_cls closure for rrcb - * @return double linked list of SPDYF_Response_Queue structures: one or - * more frames are returned based on the size of the data - */ -struct SPDYF_Response_Queue * -SPDYF_response_queue_create(bool is_data, - void *data, - size_t data_size, - struct SPDY_Response *response, - struct SPDYF_Stream *stream, - bool closestream, - SPDYF_ResponseQueueResultCallback frqcb, - void *frqcb_cls, - SPDY_ResponseResultCallback rrcb, - void *rrcb_cls); - - -/** - * Destroys SPDYF_Response_Queue structure and whatever is in it. - * - * @param response_queue to destroy - */ -void -SPDYF_response_queue_destroy(struct SPDYF_Response_Queue *response_queue); - - -/** - * Checks if the container is empty, i.e. created but no values were - * added to it. - * - * @param container - * @return SPDY_YES if empty - * SPDY_NO if not - */ -int -SPDYF_name_value_is_empty(struct SPDY_NameValue *container); - - -/** - * Transforms raw binary decomressed stream of headers - * into SPDY_NameValue, containing all of the headers and values. - * - * @param stream that is to be transformed - * @param size length of the stream - * @param container will contain the newly created SPDY_NameValue - * container. Should point to NULL. - * @return SPDY_YES on success - * SPDY_NO on memory error - * SPDY_INPUT_ERROR if the provided stream is not valid - */ -int -SPDYF_name_value_from_stream(void *stream, - size_t size, - struct SPDY_NameValue ** container); - - -/** - * Transforms array of objects of name/values tuples, containing HTTP - * headers, into raw binary stream. The resulting stream is ready to - * be compressed and sent. - * - * @param container one or more SPDY_NameValue objects. Each object - * contains multiple number of name/value tuples. - * @param num_containers length of the array - * @param stream will contain the resulting stream. Should point to NULL. - * @return length of stream or value less than 0 indicating error - */ -ssize_t -SPDYF_name_value_to_stream(struct SPDY_NameValue * container[], - int num_containers, - void **stream); - -#endif diff --git a/src/spdy2http/Makefile.am b/src/spdy2http/Makefile.am @@ -1,29 +0,0 @@ -# This Makefile.am is in the public domain -SUBDIRS = . - -AM_CFLAGS = - -if USE_COVERAGE - AM_CFLAGS += -fprofile-arcs -ftest-coverage -endif - -AM_CPPFLAGS = \ - -I$(top_srcdir) \ - -I$(top_srcdir)/src/include \ - -I$(top_srcdir)/src/applicationlayer \ - -DDATA_DIR=\"$(top_srcdir)/src/datadir/\" \ - $(LIBCURL_CPPFLAGS) - -if !HAVE_W32 -PERF_GET_CONCURRENT=perf_get_concurrent -endif - -bin_PROGRAMS = \ - microspdy2http - -microspdy2http_SOURCES = \ - proxy.c -microspdy2http_LDADD = \ - $(top_builddir)/src/microspdy/libmicrospdy.la \ - -lz \ - $(LIBCURL) diff --git a/src/spdy2http/proxy.c b/src/spdy2http/proxy.c @@ -1,1411 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file proxy.c - * @brief Translates incoming SPDY requests to http server on localhost. - * Uses libcurl. - * No error handling for curl requests. - * TODO: - * - test all options! - * - don't abort on lack of memory - * - Correct recapitalizetion of header names before giving the headers - * to curl. - * - curl does not close sockets when connection is closed and no - * new sockets are opened (they stay in CLOSE_WAIT) - * - add '/' when a user requests http://example.com . Now this is a bad - * request - * - curl returns 0 or 1 ms for timeout even when nothing will be done; - * thus the loop uses CPU for nothing - * @author Andrey Uzunov - */ - -#include "platform.h" -#include <unistd.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <string.h> -#include <stdio.h> -#include <ctype.h> -#include <errno.h> -#include "microspdy.h" -#include <curl/curl.h> -#include <assert.h> -#include <getopt.h> -#include <regex.h> - -#define ERROR_RESPONSE "502 Bad Gateway" - - -struct global_options -{ - char *http_backend; - char *cert; - char *cert_key; - char *listen_host; - unsigned int timeout; - uint16_t listen_port; - bool verbose; - bool curl_verbose; - bool transparent; - bool http10; - bool notls; - bool nodelay; - bool ipv4; - bool ipv6; -} glob_opt; - - -struct URI -{ - char * full_uri; - char * scheme; - char * host_and_port; - //char * host_and_port_for_connecting; - char * host; - char * path; - char * path_and_more; - char * query; - char * fragment; - uint16_t port; -}; - - -#define PRINT_INFO(msg) do{\ - fprintf(stdout, "%i:%s\n", __LINE__, msg);\ - fflush(stdout);\ - }\ - while(0) - - -#define PRINT_INFO2(fmt, ...) do{\ - fprintf(stdout, "%i\n", __LINE__);\ - fprintf(stdout, fmt,##__VA_ARGS__);\ - fprintf(stdout, "\n");\ - fflush(stdout);\ - }\ - while(0) - - -#define PRINT_VERBOSE(msg) do{\ - if(glob_opt.verbose){\ - fprintf(stdout, "%i:%s\n", __LINE__, msg);\ - fflush(stdout);\ - }\ - }\ - while(0) - - -#define PRINT_VERBOSE2(fmt, ...) do{\ - if(glob_opt.verbose){\ - fprintf(stdout, "%i\n", __LINE__);\ - fprintf(stdout, fmt,##__VA_ARGS__);\ - fprintf(stdout, "\n");\ - fflush(stdout);\ - }\ - }\ - while(0) - - -#define CURL_SETOPT(handle, opt, val) do{\ - int ret; \ - if(CURLE_OK != (ret = curl_easy_setopt(handle, opt, val))) \ - { \ - PRINT_INFO2("curl_easy_setopt failed (%i = %i)", opt, ret); \ - abort(); \ - } \ - }\ - while(0) - - -#define DIE(msg) do{\ - printf("FATAL ERROR (line %i): %s\n", __LINE__, msg);\ - fflush(stdout);\ - exit(EXIT_FAILURE);\ - }\ - while(0) - - -static int loop = 1; - -static CURLM *multi_handle; - -static int still_running = 0; /* keep number of running handles */ - -static regex_t uri_preg; - -static bool call_spdy_run; -static bool call_curl_run; - -int debug_num_curls; - - -struct Proxy -{ - char *url; - struct SPDY_Request *request; - struct SPDY_Response *response; - CURL *curl_handle; - struct curl_slist *curl_headers; - struct SPDY_NameValue *headers; - char *version; - char *status_msg; - void *http_body; - void *received_body; - bool *session_alive; - size_t http_body_size; - size_t received_body_size; - //ssize_t length; - int status; - //bool done; - bool receiving_done; - bool is_curl_read_paused; - bool is_with_body_data; - //bool error; - bool curl_done; - bool curl_error; - bool spdy_done; - bool spdy_error; -}; - - -static void -free_uri(struct URI * uri) -{ - if(NULL != uri) - { - free(uri->full_uri); - free(uri->scheme); - free(uri->host_and_port); - //free(uri->host_and_port_for_connecting); - free(uri->host); - free(uri->path); - free(uri->path_and_more); - free(uri->query); - free(uri->fragment); - uri->port = 0; - free(uri); - } -} - - -static int -init_parse_uri(regex_t * preg) -{ - // RFC 2396 - // ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? - /* - scheme = $2 - authority = $4 - path = $5 - query = $7 - fragment = $9 - */ - - return regcomp(preg, "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", REG_EXTENDED); -} - - -static void -deinit_parse_uri(regex_t * preg) -{ - regfree(preg); -} - - -static int -parse_uri(regex_t * preg, const char * full_uri, struct URI ** uri) -{ - //TODO memeory checks - int ret; - char *colon; - long long port; - size_t nmatch = 10; - regmatch_t pmatch[10]; - - if (0 != (ret = regexec(preg, full_uri, nmatch, pmatch, 0))) - return ret; - - *uri = malloc(sizeof(struct URI)); - if(NULL == *uri) - return -200; - - (*uri)->full_uri = strdup(full_uri); - - asprintf(&((*uri)->scheme), - "%.*s", - (int) (pmatch[2].rm_eo - pmatch[2].rm_so), - &full_uri[pmatch[2].rm_so]); - asprintf(&((*uri)->host_and_port), "%.*s", - (int) (pmatch[4].rm_eo - pmatch[4].rm_so), - &full_uri[pmatch[4].rm_so]); - asprintf(&((*uri)->path), - "%.*s", - (int) (pmatch[5].rm_eo - pmatch[5].rm_so), - &full_uri[pmatch[5].rm_so]); - asprintf(&((*uri)->path_and_more), - "%.*s", - (int) (pmatch[9].rm_eo - pmatch[5].rm_so), - &full_uri[pmatch[5].rm_so]); - asprintf(&((*uri)->query), - "%.*s", - (int) (pmatch[7].rm_eo - pmatch[7].rm_so), - &full_uri[pmatch[7].rm_so]); - asprintf(&((*uri)->fragment), - "%.*s", - (int) (pmatch[9].rm_eo - pmatch[9].rm_so), - &full_uri[pmatch[9].rm_so]); - - colon = strrchr((*uri)->host_and_port, ':'); - if(NULL == colon) - { - (*uri)->host = strdup((*uri)->host_and_port); - /*if(0 == strcasecmp("http", uri->scheme)) - { - uri->port = 80; - asprintf(&(uri->host_and_port_for_connecting), "%s:80", uri->host_and_port); - } - else if(0 == strcasecmp("https", uri->scheme)) - { - uri->port = 443; - asprintf(&(uri->host_and_port_for_connecting), "%s:443", uri->host_and_port); - } - else - { - PRINT_INFO("no standard scheme!"); - */(*uri)->port = 0; - /*uri->host_and_port_for_connecting = strdup(uri->host_and_port); - }*/ - return 0; - } - - port = atoi(colon + 1); - if(port<1 || port >= 256 * 256) - { - free_uri(*uri); - return -100; - } - (*uri)->port = port; - asprintf(&((*uri)->host), "%.*s", (int)(colon - (*uri)->host_and_port), (*uri)->host_and_port); - - return 0; -} - - -static bool -store_in_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size) -{ - if(0 == src_size) - return true; - - if(NULL == *dst) - *dst = malloc(src_size); - else - *dst = realloc(*dst, src_size + *dst_size); - if(NULL == *dst) - return false; - - memcpy(*dst + *dst_size, src, src_size); - *dst_size += src_size; - - return true; -} - - -static ssize_t -get_from_buffer(void **src, size_t *src_size, void *dst, size_t max_size) -{ - size_t ret; - void *newbody; - - if(max_size >= *src_size) - { - ret = *src_size; - newbody = NULL; - } - else - { - ret = max_size; - if(NULL == (newbody = malloc(*src_size - max_size))) - return -1; - memcpy(newbody, *src + ret, *src_size - ret); - } - memcpy(dst, *src, ret); - free(*src); - *src = newbody; - *src_size -= ret; - - return ret; -} - - -static void -catch_signal(int signal) -{ - (void)signal; - - loop = 0; -} - -static void -new_session_cb (void * cls, - struct SPDY_Session * session) -{ - (void)cls; - - bool *session_alive; - - PRINT_VERBOSE("new session"); - //TODO clean this memory - if(NULL == (session_alive = malloc(sizeof(bool)))) - { - DIE("no memory"); - } - *session_alive = true; - SPDY_set_cls_to_session(session, - session_alive); -} - -static void -session_closed_cb (void * cls, - struct SPDY_Session * session, - int by_client) -{ - (void)cls; - - bool *session_alive; - - PRINT_VERBOSE2("session closed; by client: %i", by_client); - - session_alive = SPDY_get_cls_from_session(session); - assert(NULL != session_alive); - - *session_alive = false; -} - - -static int -spdy_post_data_cb (void * cls, - struct SPDY_Request *request, - const void * buf, - size_t size, - bool more) -{ - (void)cls; - int ret; - struct Proxy *proxy = (struct Proxy *)SPDY_get_cls_from_request(request); - - if(!store_in_buffer(buf, size, &proxy->received_body, &proxy->received_body_size)) - { - PRINT_INFO("not enough memory (malloc/realloc returned NULL)"); - return 0; - } - - proxy->receiving_done = !more; - - PRINT_VERBOSE2("POST bytes from SPDY: %zu", size); - - call_curl_run = true; - - if(proxy->is_curl_read_paused) - { - if(CURLE_OK != (ret = curl_easy_pause(proxy->curl_handle, CURLPAUSE_CONT))) - { - PRINT_INFO2("curl_easy_pause returned %i", ret); - abort(); - } - PRINT_VERBOSE("curl_read_cb pause resumed"); - } - - return SPDY_YES; -} - - -ssize_t -response_callback (void *cls, - void *buffer, - size_t max, - bool *more) -{ - ssize_t ret; - struct Proxy *proxy = (struct Proxy *)cls; - - *more = true; - - assert(!proxy->spdy_error); - - if(proxy->curl_error) - { - PRINT_VERBOSE("tell spdy about the error"); - return -1; - } - - if(!proxy->http_body_size)//nothing to write now - { - PRINT_VERBOSE("nothing to write now"); - if(proxy->curl_done || proxy->curl_error) *more = false; - return 0; - } - - ret = get_from_buffer(&(proxy->http_body), &(proxy->http_body_size), buffer, max); - if(ret < 0) - { - PRINT_INFO("no memory"); - //TODO error? - return -1; - } - - if((proxy->curl_done || proxy->curl_error) && 0 == proxy->http_body_size) *more = false; - - PRINT_VERBOSE2("given bytes to microspdy: %zd", ret); - - return ret; -} - - -static void -cleanup(struct Proxy *proxy) -{ - int ret; - - //fprintf(stderr, "free proxy for %s\n", proxy->url); - - if(CURLM_OK != (ret = curl_multi_remove_handle(multi_handle, proxy->curl_handle))) - { - PRINT_INFO2("curl_multi_remove_handle failed (%i)", ret); - DIE("bug in cleanup"); - } - debug_num_curls--; - //TODO bug on ku6.com or amazon.cn - // after curl_multi_remove_handle returned CURLM_BAD_EASY_HANDLE - curl_slist_free_all(proxy->curl_headers); - curl_easy_cleanup(proxy->curl_handle); - - free(proxy->url); - free(proxy); -} - - -static void -response_done_callback(void *cls, - struct SPDY_Response *response, - struct SPDY_Request *request, - enum SPDY_RESPONSE_RESULT status, - bool streamopened) -{ - (void)streamopened; - struct Proxy *proxy = (struct Proxy *)cls; - - if(SPDY_RESPONSE_RESULT_SUCCESS != status) - { - free(proxy->http_body); - proxy->http_body = NULL; - proxy->spdy_error = true; - } - cleanup(proxy); - SPDY_destroy_request(request); - SPDY_destroy_response(response); -} - - -static size_t -curl_header_cb(void *ptr, size_t size, size_t nmemb, void *userp) -{ - size_t realsize = size * nmemb; - struct Proxy *proxy = (struct Proxy *)userp; - char *line = (char *)ptr; - char *name; - char *value; - char *status; - unsigned int i; - unsigned int pos; - int ret; - int num_values; - const char * const * values; - bool abort_it; - - //printf("curl_header_cb %s\n", line); - if(!*(proxy->session_alive)) - { - PRINT_VERBOSE("headers received, but session is dead"); - proxy->spdy_error = true; - proxy->curl_error = true; - return 0; - } - - //trailer - if(NULL != proxy->response) return 0; - - if('\r' == line[0] || '\n' == line[0]) - { - //all headers were already handled; prepare spdy frames - if(NULL == (proxy->response = SPDY_build_response_with_callback(proxy->status, - proxy->status_msg, - proxy->version, - proxy->headers, - &response_callback, - proxy, - 0))) - //256))) - DIE("no response"); - - SPDY_name_value_destroy(proxy->headers); - proxy->headers = NULL; - free(proxy->status_msg); - proxy->status_msg = NULL; - free(proxy->version); - proxy->version = NULL; - - if(SPDY_YES != SPDY_queue_response(proxy->request, - proxy->response, - true, - false, - &response_done_callback, - proxy)) - { - //DIE("no queue"); - //TODO right? - proxy->spdy_error = true; - proxy->curl_error = true; - PRINT_VERBOSE2("no queue in curl_header_cb for %s", proxy->url); - SPDY_destroy_response(proxy->response); - proxy->response = NULL; - return 0; - } - - call_spdy_run = true; - - return realsize; - } - - pos = 0; - if(NULL == proxy->version) - { - //first line from headers - //version - for(i=pos; i<realsize && ' '!=line[i]; ++i); - if(i == realsize) - DIE("error on parsing headers"); - if(NULL == (proxy->version = strndup(line, i - pos))) - DIE("No memory"); - pos = i+1; - - //status (number) - for(i=pos; i<realsize && ' '!=line[i] && '\r'!=line[i] && '\n'!=line[i]; ++i); - if(NULL == (status = strndup(&(line[pos]), i - pos))) - DIE("No memory"); - proxy->status = atoi(status); - free(status); - if(i<realsize && '\r'!=line[i] && '\n'!=line[i]) - { - //status (message) - pos = i+1; - for(i=pos; i<realsize && '\r'!=line[i] && '\n'!=line[i]; ++i); - if(NULL == (proxy->status_msg = strndup(&(line[pos]), i - pos))) - DIE("No memory"); - } - PRINT_VERBOSE2("Header line received '%s' '%i' '%s' ", proxy->version, proxy->status, proxy->status_msg); - return realsize; - } - - //other lines - //header name - for(i=pos; i<realsize && ':'!=line[i] && '\r'!=line[i] && '\n'!=line[i]; ++i) - line[i] = tolower(line[i]); //spdy requires lower case - if(NULL == (name = strndup(line, i - pos))) - DIE("No memory"); - if(0 == strcmp(SPDY_HTTP_HEADER_CONNECTION, name) - || 0 == strcmp(SPDY_HTTP_HEADER_KEEP_ALIVE, name) - || 0 == strcmp(SPDY_HTTP_HEADER_TRANSFER_ENCODING, name) - ) - { - //forbidden in spdy, ignore - free(name); - return realsize; - } - if(i == realsize || '\r'==line[i] || '\n'==line[i]) - { - //no value. is it possible? - if(SPDY_YES != SPDY_name_value_add(proxy->headers, name, "")) - DIE("SPDY_name_value_add failed"); - return realsize; - } - - //header value - pos = i+1; - while(pos<realsize && isspace(line[pos])) ++pos; //remove leading space - for(i=pos; i<realsize && '\r'!=line[i] && '\n'!=line[i]; ++i); - if(NULL == (value = strndup(&(line[pos]), i - pos))) - DIE("No memory"); - PRINT_VERBOSE2("Adding header: '%s': '%s'", name, value); - if(SPDY_YES != (ret = SPDY_name_value_add(proxy->headers, name, value))) - { - abort_it=true; - if(NULL != (values = SPDY_name_value_lookup(proxy->headers, name, &num_values))) - for(i=0; i<(unsigned int)num_values; ++i) - if(0 == strcasecmp(value, values[i])) - { - abort_it=false; - PRINT_VERBOSE2("header appears more than once with same value '%s: %s'", name, value); - break; - } - - if(abort_it) - { - PRINT_INFO2("SPDY_name_value_add failed (%i) for '%s'", ret, name); - abort(); - } - } - free(name); - free(value); - - return realsize; -} - - -static size_t -curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp) -{ - size_t realsize = size * nmemb; - struct Proxy *proxy = (struct Proxy *)userp; - - //printf("curl_write_cb %i\n", realsize); - if(!*(proxy->session_alive)) - { - PRINT_VERBOSE("data received, but session is dead"); - proxy->spdy_error = true; - proxy->curl_error = true; - return 0; - } - - if(!store_in_buffer(contents, realsize, &proxy->http_body, &proxy->http_body_size)) - { - PRINT_INFO("not enough memory (malloc/realloc returned NULL)"); - proxy->curl_error = true; - return 0; - } - /* - if(NULL == proxy->http_body) - proxy->http_body = malloc(realsize); - else - proxy->http_body = realloc(proxy->http_body, proxy->http_body_size + realsize); - if(NULL == proxy->http_body) - { - PRINT_INFO("not enough memory (realloc returned NULL)"); - return 0; - } - - memcpy(proxy->http_body + proxy->http_body_size, contents, realsize); - proxy->http_body_size += realsize; - */ - - PRINT_VERBOSE2("received bytes from curl: %zu", realsize); - - call_spdy_run = true; - - return realsize; -} - - -static size_t -curl_read_cb(void *ptr, size_t size, size_t nmemb, void *userp) -{ - ssize_t ret; - size_t max = size * nmemb; - struct Proxy *proxy = (struct Proxy *)userp; - //void *newbody; - - - if((proxy->receiving_done && !proxy->received_body_size) || !proxy->is_with_body_data || max < 1) - { - PRINT_VERBOSE("curl_read_cb last call"); - return 0; - } - - if(!*(proxy->session_alive)) - { - PRINT_VERBOSE("POST is still being sent, but session is dead"); - return CURL_READFUNC_ABORT; - } - - if(!proxy->received_body_size)//nothing to write now - { - PRINT_VERBOSE("curl_read_cb called paused"); - proxy->is_curl_read_paused = true; - return CURL_READFUNC_PAUSE;//TODO curl pause should be used - } - - ret = get_from_buffer(&(proxy->received_body), &(proxy->received_body_size), ptr, max); - if(ret < 0) - { - PRINT_INFO("no memory"); - return CURL_READFUNC_ABORT; - } - - /* - if(max >= proxy->received_body_size) - { - ret = proxy->received_body_size; - newbody = NULL; - } - else - { - ret = max; - if(NULL == (newbody = malloc(proxy->received_body_size - max))) - { - PRINT_INFO("no memory"); - return CURL_READFUNC_ABORT; - } - memcpy(newbody, proxy->received_body + max, proxy->received_body_size - max); - } - memcpy(ptr, proxy->received_body, ret); - free(proxy->received_body); - proxy->received_body = newbody; - proxy->received_body_size -= ret; - * */ - - PRINT_VERBOSE2("given POST bytes to curl: %zd", ret); - - return ret; -} - - -static int -iterate_cb (void *cls, const char *name, const char * const * value, int num_values) -{ - struct Proxy *proxy = (struct Proxy *)cls; - struct curl_slist **curl_headers = (&(proxy->curl_headers)); - char *line; - int line_len = strlen(name) + 3; //+ ": \0" - int i; - - for(i=0; i<num_values; ++i) - { - if(i) line_len += 2; //", " - line_len += strlen(value[i]); - } - - if(NULL == (line = malloc(line_len)))//no recovery - DIE("No memory"); - line[0] = 0; - - strcat(line, name); - strcat(line, ": "); - //all spdy header names are lower case; - //for simplicity here we just capitalize the first letter - line[0] = toupper(line[0]); - - for(i=0; i<num_values; ++i) - { - if(i) strcat(line, ", "); - PRINT_VERBOSE2("Adding request header: '%s' len %ld", value[i], strlen(value[i])); - strcat(line, value[i]); - } - PRINT_VERBOSE2("Adding request header: '%s'", line); - if(NULL == (*curl_headers = curl_slist_append(*curl_headers, line))) - DIE("curl_slist_append failed"); - free(line); - - return SPDY_YES; -} - - -static void -standard_request_handler(void *cls, - struct SPDY_Request * request, - uint8_t priority, - const char *method, - const char *path, - const char *version, - const char *host, - const char *scheme, - struct SPDY_NameValue * headers, - bool more) -{ - (void)cls; - (void)priority; - (void)host; - (void)scheme; - - struct Proxy *proxy; - int ret; - struct URI *uri; - struct SPDY_Session *session; - - proxy = SPDY_get_cls_from_request(request); - if(NULL != proxy) - { - //ignore trailers or more headers - return; - } - - PRINT_VERBOSE2("received request for '%s %s %s'\n", method, path, version); - - //TODO not freed once in a while - if(NULL == (proxy = malloc(sizeof(struct Proxy)))) - DIE("No memory"); - memset(proxy, 0, sizeof(struct Proxy)); - - //fprintf(stderr, "new proxy for %s\n", path); - - session = SPDY_get_session_for_request(request); - assert(NULL != session); - proxy->session_alive = SPDY_get_cls_from_session(session); - assert(NULL != proxy->session_alive); - - SPDY_set_cls_to_request(request, proxy); - - proxy->request = request; - proxy->is_with_body_data = more; - if(NULL == (proxy->headers = SPDY_name_value_create())) - DIE("No memory"); - - if(glob_opt.transparent) - { - if(NULL != glob_opt.http_backend) //use always same host - ret = asprintf(&(proxy->url),"%s://%s%s", scheme, glob_opt.http_backend, path); - else //use host header - ret = asprintf(&(proxy->url),"%s://%s%s", scheme, host, path); - if(-1 == ret) - DIE("No memory"); - - ret = parse_uri(&uri_preg, proxy->url, &uri); - if(ret != 0) - DIE("parsing built uri failed"); - } - else - { - ret = parse_uri(&uri_preg, path, &uri); - PRINT_INFO2("path %s '%s' '%s'", path, uri->scheme, uri->host); - if(ret != 0 || !strlen(uri->scheme) || !strlen(uri->host)) - DIE("parsing received uri failed"); - - if(NULL != glob_opt.http_backend) //use backend host - { - ret = asprintf(&(proxy->url),"%s://%s%s", uri->scheme, glob_opt.http_backend, uri->path_and_more); - if(-1 == ret) - DIE("No memory"); - } - else //use request path - if(NULL == (proxy->url = strdup(path))) - DIE("No memory"); - } - - free_uri(uri); - - PRINT_VERBOSE2("curl will request '%s'", proxy->url); - - SPDY_name_value_iterate(headers, &iterate_cb, proxy); - - if(NULL == (proxy->curl_handle = curl_easy_init())) - { - PRINT_INFO("curl_easy_init failed"); - abort(); - } - - if(glob_opt.curl_verbose) - CURL_SETOPT(proxy->curl_handle, CURLOPT_VERBOSE, 1); - - if(0 == strcmp(SPDY_HTTP_METHOD_POST,method)) - { - if(NULL == (proxy->curl_headers = curl_slist_append(proxy->curl_headers, "Expect:"))) - DIE("curl_slist_append failed"); - CURL_SETOPT(proxy->curl_handle, CURLOPT_POST, 1); - CURL_SETOPT(proxy->curl_handle, CURLOPT_READFUNCTION, curl_read_cb); - CURL_SETOPT(proxy->curl_handle, CURLOPT_READDATA, proxy); - } - - if(glob_opt.timeout) - CURL_SETOPT(proxy->curl_handle, CURLOPT_TIMEOUT, glob_opt.timeout); - CURL_SETOPT(proxy->curl_handle, CURLOPT_URL, proxy->url); - if(glob_opt.http10) - CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEFUNCTION, curl_write_cb); - CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEDATA, proxy); - CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERFUNCTION, curl_header_cb); - CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERDATA, proxy); - CURL_SETOPT(proxy->curl_handle, CURLOPT_PRIVATE, proxy); - CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTPHEADER, proxy->curl_headers); - CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);//TODO - CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); - if(glob_opt.ipv4 && !glob_opt.ipv6) - CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - else if(glob_opt.ipv6 && !glob_opt.ipv4) - CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); - - if(CURLM_OK != (ret = curl_multi_add_handle(multi_handle, proxy->curl_handle))) - { - PRINT_INFO2("curl_multi_add_handle failed (%i)", ret); - abort(); - } - debug_num_curls++; - - //~5ms additional latency for calling this - if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running)) - && CURLM_CALL_MULTI_PERFORM != ret) - { - PRINT_INFO2("curl_multi_perform failed (%i)", ret); - abort(); - } - - call_curl_run = true; -} - - -static int -run () -{ - unsigned long long timeoutlong = 0; - unsigned long long timeout_spdy = 0; - long timeout_curl = -1; - struct timeval timeout; - int ret; - int ret_curl; - int ret_spdy; - fd_set rs; - fd_set ws; - fd_set es; - int maxfd = -1; - int maxfd_curl = -1; - struct SPDY_Daemon *daemon; - CURLMsg *msg; - int msgs_left; - struct Proxy *proxy; - struct sockaddr_in *addr; - struct addrinfo hints; - char service[NI_MAXSERV]; - struct addrinfo *gai; - enum SPDY_IO_SUBSYSTEM io = glob_opt.notls ? SPDY_IO_SUBSYSTEM_RAW : SPDY_IO_SUBSYSTEM_OPENSSL; - enum SPDY_DAEMON_FLAG flags = SPDY_DAEMON_FLAG_NO; - //struct SPDY_Response *error_response; - char *curl_private; - - signal(SIGPIPE, SIG_IGN); - - if (signal(SIGINT, catch_signal) == SIG_ERR) - PRINT_VERBOSE("signal failed"); - - srand(time(NULL)); - if(init_parse_uri(&uri_preg)) - DIE("Regexp compilation failed"); - - SPDY_init(); - - if(glob_opt.nodelay) - flags |= SPDY_DAEMON_FLAG_NO_DELAY; - - if(NULL == glob_opt.listen_host) - { - daemon = SPDY_start_daemon(glob_opt.listen_port, - glob_opt.cert, - glob_opt.cert_key, - &new_session_cb, - &session_closed_cb, - &standard_request_handler, - &spdy_post_data_cb, - NULL, - SPDY_DAEMON_OPTION_SESSION_TIMEOUT, - 1800, - SPDY_DAEMON_OPTION_IO_SUBSYSTEM, - io, - SPDY_DAEMON_OPTION_FLAGS, - flags, - SPDY_DAEMON_OPTION_END); - } - else - { - snprintf (service, sizeof(service), "%u", glob_opt.listen_port); - memset (&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - - ret = getaddrinfo(glob_opt.listen_host, service, &hints, &gai); - if(ret != 0) - DIE("problem with specified host"); - - addr = (struct sockaddr_in *) gai->ai_addr; - - daemon = SPDY_start_daemon(0, - glob_opt.cert, - glob_opt.cert_key, - &new_session_cb, - &session_closed_cb, - &standard_request_handler, - &spdy_post_data_cb, - NULL, - SPDY_DAEMON_OPTION_SESSION_TIMEOUT, - 1800, - SPDY_DAEMON_OPTION_IO_SUBSYSTEM, - io, - SPDY_DAEMON_OPTION_FLAGS, - flags, - SPDY_DAEMON_OPTION_SOCK_ADDR, - addr, - //SPDY_DAEMON_OPTION_MAX_NUM_FRAMES, - //1, - SPDY_DAEMON_OPTION_END); - } - - if(NULL==daemon){ - printf("no daemon\n"); - return 1; - } - - multi_handle = curl_multi_init(); - if(NULL==multi_handle) - DIE("no multi_handle"); - - timeout.tv_usec = 0; - - do - { - FD_ZERO(&rs); - FD_ZERO(&ws); - FD_ZERO(&es); - - PRINT_VERBOSE2("num curls %i", debug_num_curls); - - ret_spdy = SPDY_get_timeout(daemon, &timeout_spdy); - if(SPDY_NO == ret_spdy || timeout_spdy > 5000) - timeoutlong = 5000; - else - timeoutlong = timeout_spdy; - PRINT_VERBOSE2("SPDY timeout %lld; %i", timeout_spdy, ret_spdy); - - if(CURLM_OK != (ret_curl = curl_multi_timeout(multi_handle, &timeout_curl))) - { - PRINT_VERBOSE2("curl_multi_timeout failed (%i)", ret_curl); - //curl_timeo = timeoutlong; - } - else if(timeout_curl >= 0 && timeoutlong > (unsigned long)timeout_curl) - timeoutlong = (unsigned long)timeout_curl; - - PRINT_VERBOSE2("curl timeout %ld", timeout_curl); - - timeout.tv_sec = timeoutlong / 1000; - timeout.tv_usec = (timeoutlong % 1000) * 1000; - - maxfd = SPDY_get_fdset (daemon, - &rs, - &ws, - &es); - assert(-1 != maxfd); - - if(CURLM_OK != (ret = curl_multi_fdset(multi_handle, &rs, - &ws, - &es, &maxfd_curl))) - { - PRINT_INFO2("curl_multi_fdset failed (%i)", ret); - abort(); - } - - if(maxfd_curl > maxfd) - maxfd = maxfd_curl; - - PRINT_VERBOSE2("timeout before %lld %lld", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec); - ret = select(maxfd+1, &rs, &ws, &es, &timeout); - PRINT_VERBOSE2("timeout after %lld %lld; ret is %i", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec, ret); - - /*switch(ret) { - case -1: - PRINT_INFO2("select error: %i", errno); - break; - case 0: - break; - default:*/ - - //the second part should not happen with current implementation - if(ret > 0 || (SPDY_YES == ret_spdy && 0 == timeout_spdy)) - { - PRINT_VERBOSE("run spdy"); - SPDY_run(daemon); - call_spdy_run = false; - } - - //if(ret > 0 || (CURLM_OK == ret_curl && 0 == timeout_curl) || call_curl_run) - { - PRINT_VERBOSE("run curl"); - if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running)) - && CURLM_CALL_MULTI_PERFORM != ret) - { - PRINT_INFO2("curl_multi_perform failed (%i)", ret); - abort(); - } - call_curl_run = false; - } - /*break; - }*/ - - while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) { - if (msg->msg == CURLMSG_DONE) { - PRINT_VERBOSE("A curl handler is done"); - if(CURLE_OK != (ret = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &curl_private))) - { - PRINT_INFO2("err %i",ret); - abort(); - } - assert(NULL != curl_private); - proxy = (struct Proxy *)curl_private; - if(CURLE_OK == msg->data.result) - { - proxy->curl_done = true; - call_spdy_run = true; - //TODO what happens with proxy when the client resets a stream - //and response_done is not yet set for the last frame? is it - //possible? - } - else - { - PRINT_VERBOSE2("bad curl result (%i) for '%s'", msg->data.result, proxy->url); - if(proxy->spdy_done || proxy->spdy_error || (NULL == proxy->response && !*(proxy->session_alive))) - { - PRINT_VERBOSE("cleaning"); - SPDY_name_value_destroy(proxy->headers); - SPDY_destroy_request(proxy->request); - SPDY_destroy_response(proxy->response); - cleanup(proxy); - } - else if(NULL == proxy->response && *(proxy->session_alive)) - { - //generate error for the client - PRINT_VERBOSE("will send Bad Gateway"); - SPDY_name_value_destroy(proxy->headers); - proxy->headers = NULL; - if(NULL == (proxy->response = SPDY_build_response(SPDY_HTTP_BAD_GATEWAY, - NULL, - SPDY_HTTP_VERSION_1_1, - NULL, - ERROR_RESPONSE, - strlen(ERROR_RESPONSE)))) - DIE("no response"); - if(SPDY_YES != SPDY_queue_response(proxy->request, - proxy->response, - true, - false, - &response_done_callback, - proxy)) - { - //clean and forget - PRINT_VERBOSE("cleaning"); - SPDY_destroy_request(proxy->request); - SPDY_destroy_response(proxy->response); - cleanup(proxy); - } - } - else - { - proxy->curl_error = true; - } - call_spdy_run = true; - //TODO spdy should be notified to send RST_STREAM - } - } - else PRINT_INFO("shouldn't happen"); - } - - if(call_spdy_run) - { - PRINT_VERBOSE("second call to SPDY_run"); - SPDY_run(daemon); - call_spdy_run = false; - } - - if(glob_opt.verbose) - { - -#ifdef HAVE_CLOCK_GETTIME -#ifdef CLOCK_MONOTONIC - struct timespec ts; - - if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) - PRINT_VERBOSE2 ("time now %lld %lld", - (unsigned long long) ts.tv_sec, - (unsigned long long) ts.tv_nsec); -#endif -#endif - } - - } - while(loop); - - SPDY_stop_daemon(daemon); - - curl_multi_cleanup(multi_handle); - - SPDY_deinit(); - - deinit_parse_uri(&uri_preg); - - return 0; -} - - -static void -display_usage() -{ - printf( - "Usage: microspdy2http -p <PORT> [-c <CERTIFICATE>] [-k <CERT-KEY>]\n" - " [-rvh0DtT] [-b <HTTP-SERVER>] [-l <HOST>]\n\n" - "OPTIONS:\n" - " -p, --port Listening port.\n" - " -l, --host Listening host. If not set, will listen on [::]\n" - " -c, --certificate Path to a certificate file. Requiered if\n" - " --no-tls is not set.\n" - " -k, --certificate-key Path to a key file for the certificate.\n" - " Requiered if --no-tls is not set.\n" - " -b, --backend-server If set, the proxy will connect always to it.\n" - " Otherwise the proxy will connect to the URL\n" - " which is specified in the path or 'Host:'.\n" - " -v, --verbose Print debug information.\n" - " -r, --no-tls Do not use TLS. Client must use SPDY/3.\n" - " -h, --curl-verbose Print debug information for curl.\n" - " -0, --http10 Prefer HTTP/1.0 connections to the next hop.\n" - " -D, --no-delay This makes sense only if --no-tls is used.\n" - " TCP_NODELAY will be used for all sessions' sockets.\n" - " -4, --curl-ipv4 Curl may use IPv4 to connect to the final destination.\n" - " -6, --curl-ipv6 Curl may use IPv6 to connect to the final destination.\n" - " If neither --curl-ipv4 nor --curl-ipv6 is set,\n" - " both will be used by default.\n" - " -T, --timeout Maximum time in seconds for each HTTP transfer.\n" - " Use 0 for no timeout; this is the default value.\n" - " -t, --transparent If set, the proxy will fetch an URL which\n" - " is based on 'Host:' header and requested path.\n" - " Otherwise, full URL in the requested path is required.\n\n" - - ); -} - - -int -main (int argc, char *const *argv) -{ - - int getopt_ret; - int option_index; - struct option long_options[] = { - {"port", required_argument, 0, 'p'}, - {"certificate", required_argument, 0, 'c'}, - {"certificate-key", required_argument, 0, 'k'}, - {"backend-server", required_argument, 0, 'b'}, - {"no-tls", no_argument, 0, 'r'}, - {"verbose", no_argument, 0, 'v'}, - {"curl-verbose", no_argument, 0, 'h'}, - {"http10", no_argument, 0, '0'}, - {"no-delay", no_argument, 0, 'D'}, - {"transparent", no_argument, 0, 't'}, - {"curl-ipv4", no_argument, 0, '4'}, - {"curl-ipv6", no_argument, 0, '6'}, - {"timeout", required_argument, 0, 'T'}, - {0, 0, 0, 0} - }; - - while (1) - { - getopt_ret = getopt_long( argc, argv, "p:l:c:k:b:rv0Dth46T:", long_options, &option_index); - if (getopt_ret == -1) - break; - - switch(getopt_ret) - { - case 'p': - glob_opt.listen_port = atoi(optarg); - break; - - case 'l': - glob_opt.listen_host= strdup(optarg); - if(NULL == glob_opt.listen_host) - return 1; - break; - - case 'c': - glob_opt.cert = strdup(optarg); - break; - - case 'k': - glob_opt.cert_key = strdup(optarg); - break; - - case 'b': - glob_opt.http_backend = strdup(optarg); - if(NULL == glob_opt.http_backend) - return 1; - break; - - case 'r': - glob_opt.notls = true; - break; - - case 'v': - glob_opt.verbose = true; - break; - - case 'h': - glob_opt.curl_verbose = true; - break; - - case '0': - glob_opt.http10 = true; - break; - - case 'D': - glob_opt.nodelay = true; - break; - - case 't': - glob_opt.transparent = true; - break; - - case '4': - glob_opt.ipv4 = true; - break; - - case '6': - glob_opt.ipv6 = true; - break; - - case 'T': - glob_opt.timeout = atoi(optarg); - break; - - case 0: - PRINT_INFO("0 from getopt"); - break; - - case '?': - display_usage(); - return 1; - - default: - DIE("default from getopt"); - } - } - - if( - 0 == glob_opt.listen_port - || (!glob_opt.notls && (NULL == glob_opt.cert || NULL == glob_opt.cert_key)) - //|| !glob_opt.transparent && NULL != glob_opt.http_backend - ) - { - display_usage(); - return 1; - } - - return run(); -} - diff --git a/src/testspdy/Makefile.am b/src/testspdy/Makefile.am @@ -1,115 +0,0 @@ -# This Makefile.am is in the public domain -SUBDIRS = . - -AM_CFLAGS = -DDATA_DIR=\"$(top_srcdir)/src/datadir/\" - -if USE_COVERAGE - AM_CFLAGS += -fprofile-arcs -ftest-coverage -endif - -AM_CPPFLAGS = \ - -I$(top_srcdir) \ - -I$(top_srcdir)/src/include \ - -I$(top_srcdir)/src/applicationlayer \ - $(LIBCURL_CPPFLAGS) - -if !HAVE_W32 -PERF_GET_CONCURRENT=perf_get_concurrent -endif - -if ENABLE_SPDY -if HAVE_OPENSSL -check_PROGRAMS = \ - test_daemon_start_stop \ - test_daemon_start_stop_many \ - test_struct_namevalue - -if HAVE_SPDYLAY -check_PROGRAMS += \ - test_new_connection \ - test_request_response \ - test_notls \ - test_request_response_with_callback \ - test_misc \ - test_session_timeout - #test_requests_with_assets -if HAVE_CURL_BINARY -check_PROGRAMS += \ - test_proxies -endif -endif -endif -endif - - -TESTS = $(check_PROGRAMS) - - -SPDY_SOURCES= \ - common.h common.c - -SPDY_LDADD= \ - $(top_builddir)/src/microspdy/libmicrospdy.la \ - -lz - -test_daemon_start_stop_SOURCES = \ - test_daemon_start_stop.c \ - $(SPDY_SOURCES) -test_daemon_start_stop_LDADD = $(SPDY_LDADD) - -test_daemon_start_stop_many_SOURCES = \ - test_daemon_start_stop_many.c \ - $(SPDY_SOURCES) -test_daemon_start_stop_many_LDADD = $(SPDY_LDADD) - -test_struct_namevalue_SOURCES = \ - test_struct_namevalue.c \ - $(SPDY_SOURCES) -test_struct_namevalue_LDADD = $(SPDY_LDADD) - -if HAVE_SPDYLAY -test_new_connection_SOURCES = \ - test_new_connection.c \ - $(SPDY_SOURCES) -test_new_connection_LDADD = $(SPDY_LDADD) \ - -lspdylay - -test_request_response_SOURCES = \ - test_request_response.c \ - $(SPDY_SOURCES) -test_request_response_LDADD = $(SPDY_LDADD) \ - -lspdylay - -test_notls_SOURCES = \ - test_notls.c \ - $(SPDY_SOURCES) -test_notls_LDADD = $(SPDY_LDADD) \ - -lspdylay - -test_request_response_with_callback_SOURCES = \ - test_request_response_with_callback.c \ - $(SPDY_SOURCES) -test_request_response_with_callback_LDADD = $(SPDY_LDADD) - -#test_requests_with_assets_SOURCES = \ -# test_requests_with_assets.c \ -# $(SPDY_SOURCES) -#test_requests_with_assets_LDADD = $(SPDY_LDADD) - -test_misc_SOURCES = \ - test_misc.c \ - $(SPDY_SOURCES) -test_misc_LDADD = $(SPDY_LDADD) - -test_session_timeout_SOURCES = \ - test_session_timeout.c \ - $(SPDY_SOURCES) -test_session_timeout_LDADD = $(SPDY_LDADD) - - -test_proxies_SOURCES = \ - test_proxies.c \ - $(SPDY_SOURCES) -test_proxies_LDADD = $(SPDY_LDADD) - -endif diff --git a/src/testspdy/common.c b/src/testspdy/common.c @@ -1,59 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file common.c - * @brief Common functions used by the tests. - * @author Andrey Uzunov - */ - - -#include "common.h" -#include <sys/time.h> - -#ifdef __GNUC__ -#define FUNC_CONSTRUCTOR(f) static void __attribute__ ((constructor)) f -#define FUNC_DESTRUCTOR(f) static void __attribute__ ((destructor)) f -#else // !__GNUC__ -#define FUNC_CONSTRUCTOR(f) _MHD_EXTERN void f -#define FUNC_DESTRUCTOR(f) _MHD_EXTERN void f -#endif // __GNUC__ - -FUNC_CONSTRUCTOR (constructor)() -{ - printf("\nTEST START -------------------------------------------------------\n"); -} - -FUNC_DESTRUCTOR (destructor)() -{ - printf("------------------------------------------------------- TEST END\n"); -} - -uint16_t -get_port(uint16_t min) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - if(2 > min) min=2; - uint16_t port = min + (tv.tv_usec+10) % ((1 << 16) - min); - - //port = 12345; - printf("Port used: %i\n", port); - - return port; -} diff --git a/src/testspdy/common.h b/src/testspdy/common.h @@ -1,38 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file common.h - * @brief Common functions used by the tests. - * @author Andrey Uzunov - */ - -#include <stdlib.h> -#include <unistd.h> -#include <stdint.h> -#include <stdio.h> - -#define FAIL_TEST(msg) do{\ - printf("%i:%s\n", __LINE__, msg);\ - fflush(stdout);\ - exit(-10);\ - }\ - while(0) - -uint16_t -get_port(uint16_t min); diff --git a/src/testspdy/test_daemon_start_stop.c b/src/testspdy/test_daemon_start_stop.c @@ -1,49 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file daemon_start_stop.c - * @brief starts and stops a SPDY daemon - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "microspdy.h" -#include "common.h" - -int -main() -{ - SPDY_init(); - - struct SPDY_Daemon *daemon = SPDY_start_daemon(get_port(16123), - DATA_DIR "cert-and-key.pem", - DATA_DIR "cert-and-key.pem", - NULL,NULL,NULL,NULL,NULL,SPDY_DAEMON_OPTION_END); - - if(NULL==daemon){ - printf("no daemon\n"); - return 1; - } - - SPDY_stop_daemon(daemon); - - SPDY_deinit(); - - return 0; -} diff --git a/src/testspdy/test_daemon_start_stop_many.c b/src/testspdy/test_daemon_start_stop_many.c @@ -1,66 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file daemon_start_stop_many.c - * @brief starts and stops several SPDY daemons, reusing port numbers - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "microspdy.h" -#include "common.h" - -int -main() -{ - int i; - int j; - int num_daemons = 3; - int num_tries = 5; - int port = get_port(15123); - struct SPDY_Daemon *daemon[num_daemons]; - - SPDY_init(); - - for(i=0; i<num_tries; ++i) - { - for(j=0;j<num_daemons;++j) - { - daemon[j] = SPDY_start_daemon(port + j, - DATA_DIR "cert-and-key.pem", - DATA_DIR "cert-and-key.pem", - NULL,NULL,NULL,NULL,NULL,SPDY_DAEMON_OPTION_END); - - if(NULL==daemon[j]){ - printf("no daemon\n"); - return 1; - } - } - - - for(j=0;j<num_daemons;++j) - { - SPDY_stop_daemon(daemon[j]); - } - } - - SPDY_deinit(); - - return 0; -} diff --git a/src/testspdy/test_misc.c b/src/testspdy/test_misc.c @@ -1,289 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file misc.c - * @brief tests a lot of small calls and callbacks. TODO mention what - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "microspdy.h" -#include "stdio.h" -#include <sys/wait.h> -#include "common.h" - -int port; - -#define HTML "<html><head>\ -<link href=\"main.css\" rel=\"stylesheet\" type=\"text/css\" />\ -</head><body>This is libmicrospdy</body></html>" - -#define CSS "body{font-size:15px}" - -#define SESSION_CLS "1234567890" - -#define REQUEST_CLS "1234567890REQ" - -pid_t parent; -pid_t child; - -struct SPDY_Session *session1; -struct SPDY_Session *session2; - -void -killchild() -{ - kill(child, SIGKILL); - exit(1); -} - -void -killparent() -{ - kill(parent, SIGKILL); - _exit(1); -} - - -void -create_child() -{ - parent = getpid(); - - child = fork(); - if (-1 == child) - { - fprintf(stderr, "can't fork, error %d\n", errno); - exit(EXIT_FAILURE); - } - - if (child == 0) - { - int devnull; - char *uri; - fflush(stdout); - devnull = open("/dev/null", O_WRONLY); - if (-1 == devnull) - abort (); - if (1 != devnull) - { - dup2(devnull, 1); - close(devnull); - } - asprintf(&uri,"https://127.0.0.1:%i/",port); - execlp("spdycat", "spdycat","-anv",uri,NULL ); - printf("execlp failed\n"); - killparent(); - } -} - -void -response_done_callback(void *cls, - struct SPDY_Response * response, - struct SPDY_Request * request, - enum SPDY_RESPONSE_RESULT status, - bool streamopened) -{ - (void)status; - (void)streamopened; - - if(strcmp(cls,"/main.css")) - { - session1 = SPDY_get_session_for_request(request); - if(NULL == session1) - { - printf("SPDY_get_session_for_request failed\n"); - killchild(); - } - - char *session_cls = strdup(SESSION_CLS); - SPDY_set_cls_to_session(session1,session_cls); - } - else - { - session2 = SPDY_get_session_for_request(request); - if(session1 != session2) - { - printf("SPDY_get_session_for_request failed the second time\n"); - killchild(); - } - printf("SPDY_get_session_for_request tested...\n"); - - void *session_cls = SPDY_get_cls_from_session(session2); - if(NULL == session_cls || strcmp(session_cls, SESSION_CLS)) - { - printf("SPDY_get_cls_from_session failed\n"); - killchild(); - } - printf("SPDY_set_cls_to_session tested...\n"); - printf("SPDY_get_cls_from_session tested...\n"); - - void *request_cls = SPDY_get_cls_from_request(request); - if(NULL == request_cls || strcmp(request_cls, REQUEST_CLS)) - { - printf("SPDY_get_cls_from_request failed\n"); - killchild(); - } - printf("SPDY_set_cls_to_request tested...\n"); - printf("SPDY_get_cls_from_request tested...\n"); - } - - SPDY_destroy_request(request); - SPDY_destroy_response(response); - free(cls); -} - -void -standard_request_handler(void *cls, - struct SPDY_Request * request, - uint8_t priority, - const char *method, - const char *path, - const char *version, - const char *host, - const char *scheme, - struct SPDY_NameValue * headers, - bool more) -{ - (void)cls; - (void)request; - (void)priority; - (void)host; - (void)scheme; - (void)headers; - (void)method; - (void)version; - (void)more; - - struct SPDY_Response *response=NULL; - char *cls_path = strdup(path); - - if(strcmp(path,"/main.css")==0) - { - char *request_cls = strdup(REQUEST_CLS); - SPDY_set_cls_to_request(request,request_cls); - response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,CSS,strlen(CSS)); - } - else - { - response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,HTML,strlen(HTML)); - } - - if(NULL==response){ - fprintf(stdout,"no response obj\n"); - killchild(); - } - - if(SPDY_queue_response(request,response,true,false,&response_done_callback,cls_path)!=SPDY_YES) - { - fprintf(stdout,"queue\n"); - killchild(); - } -} - -int -parentproc() -{ - int childstatus; - unsigned long long timeoutlong=0; - struct timeval timeout; - int ret; - fd_set read_fd_set; - fd_set write_fd_set; - fd_set except_fd_set; - int maxfd = -1; - struct SPDY_Daemon *daemon; - - daemon = SPDY_start_daemon(port, - DATA_DIR "cert-and-key.pem", - DATA_DIR "cert-and-key.pem", - NULL, - NULL, - &standard_request_handler, - NULL, - NULL, - SPDY_DAEMON_OPTION_SESSION_TIMEOUT, - 1800, - SPDY_DAEMON_OPTION_END); - - if(NULL==daemon){ - printf("no daemon\n"); - return 1; - } - - create_child(); - - do - { - FD_ZERO(&read_fd_set); - FD_ZERO(&write_fd_set); - FD_ZERO(&except_fd_set); - - ret = SPDY_get_timeout(daemon, &timeoutlong); - if(SPDY_NO == ret || timeoutlong > 1000) - { - timeout.tv_sec = 1; - timeout.tv_usec = 0; - } - else - { - timeout.tv_sec = timeoutlong / 1000; - timeout.tv_usec = (timeoutlong % 1000) * 1000; - } - - maxfd = SPDY_get_fdset (daemon, - &read_fd_set, - &write_fd_set, - &except_fd_set); - - ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); - - switch(ret) { - case -1: - printf("select error: %i\n", errno); - break; - case 0: - - break; - default: - SPDY_run(daemon); - - break; - } - } - while(waitpid(child,&childstatus,WNOHANG) != child); - - SPDY_stop_daemon(daemon); - - return WEXITSTATUS(childstatus); -} - - -int -main() -{ - port = get_port(13123); - SPDY_init(); - - int ret = parentproc(); - - SPDY_deinit(); - - return ret; -} diff --git a/src/testspdy/test_new_connection.c b/src/testspdy/test_new_connection.c @@ -1,1012 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file test_new_connection.c - * @brief tests new connection callback. spdycli.c - * code is reused here - * @author Andrey Uzunov - * @author Tatsuhiro Tsujikawa - */ - -//TODO child exits with ret val 1 sometimes - -#include "platform.h" -#include "microspdy.h" -#include <sys/wait.h> -#include "common.h" - -#define RESPONSE_BODY "<html><body><b>Hi, this is libmicrospdy!</b></body></html>" - -#define CLS "anything" - -int port; -int loop = 1; - -pid_t parent; -pid_t child; - -int -spdylay_printf(const char *format, ...) -{ - (void)format; - - return 0; -} - -int -spdylay_fprintf(FILE *stream, const char *format, ...) -{ - (void)stream; - (void)format; - - return 0; -} - -void -killchild(int pid, char *message) -{ - printf("%s\n",message); - kill(pid, SIGKILL); - exit(1); -} - -void -killparent(int pid, char *message) -{ - printf("%s\n",message); - kill(pid, SIGKILL); - _exit(2); -} - - -/***** - * start of code needed to utilize spdylay - */ - -#include <stdint.h> -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <poll.h> -#include <signal.h> -#include <stdio.h> -#include <assert.h> - -#include <spdylay/spdylay.h> - -#include <openssl/ssl.h> -#include <openssl/err.h> - -enum { - IO_NONE, - WANT_READ, - WANT_WRITE -}; - -struct Connection { - SSL *ssl; - spdylay_session *session; - /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it - needs more output; or IO_NONE. This is necessary because SSL/TLS - re-negotiation is possible at any time. Spdylay API offers - similar functions like spdylay_session_want_read() and - spdylay_session_want_write() but they do not take into account - SSL connection. */ - int want_io; -}; - -struct Request { - char *host; - uint16_t port; - /* In this program, path contains query component as well. */ - char *path; - /* This is the concatenation of host and port with ":" in - between. */ - char *hostport; - /* Stream ID for this request. */ - int32_t stream_id; - /* The gzip stream inflater for the compressed response. */ - spdylay_gzip *inflater; -}; - -struct URI { - const char *host; - size_t hostlen; - uint16_t port; - /* In this program, path contains query component as well. */ - const char *path; - size_t pathlen; - const char *hostport; - size_t hostportlen; -}; - -/* - * Returns copy of string |s| with the length |len|. The returned - * string is NULL-terminated. - */ -static char* strcopy(const char *s, size_t len) -{ - char *dst; - dst = malloc(len+1); - if (NULL == dst) - abort (); - memcpy(dst, s, len); - dst[len] = '\0'; - return dst; -} - -/* - * Prints error message |msg| and exit. - */ -static void die(const char *msg) -{ - fprintf(stderr, "FATAL: %s\n", msg); - exit(EXIT_FAILURE); -} - -/* - * Prints error containing the function name |func| and message |msg| - * and exit. - */ -static void dief(const char *func, const char *msg) -{ - fprintf(stderr, "FATAL: %s: %s\n", func, msg); - exit(EXIT_FAILURE); -} - -/* - * Prints error containing the function name |func| and error code - * |error_code| and exit. - */ -static void diec(const char *func, int error_code) -{ - fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code, - spdylay_strerror(error_code)); - exit(EXIT_FAILURE); -} - -/* - * Check response is content-encoding: gzip. We need this because SPDY - * client is required to support gzip. - */ -static void check_gzip(struct Request *req, char **nv) -{ - int gzip = 0; - size_t i; - for(i = 0; nv[i]; i += 2) { - if(strcmp("content-encoding", nv[i]) == 0) { - gzip = strcmp("gzip", nv[i+1]) == 0; - break; - } - } - if(gzip) { - int rv; - if(req->inflater) { - return; - } - rv = spdylay_gzip_inflate_new(&req->inflater); - if(rv != 0) { - die("Can't allocate inflate stream."); - } - } -} - -/* - * The implementation of spdylay_send_callback type. Here we write - * |data| with size |length| to the network and return the number of - * bytes actually written. See the documentation of - * spdylay_send_callback for the details. - */ -static ssize_t send_callback(spdylay_session *session, - const uint8_t *data, size_t length, int flags, - void *user_data) -{ - (void)session; - (void)flags; - - struct Connection *connection; - ssize_t rv; - connection = (struct Connection*)user_data; - connection->want_io = IO_NONE; - ERR_clear_error(); - rv = SSL_write(connection->ssl, data, length); - if(rv < 0) { - int err = SSL_get_error(connection->ssl, rv); - if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { - connection->want_io = (err == SSL_ERROR_WANT_READ ? - WANT_READ : WANT_WRITE); - rv = SPDYLAY_ERR_WOULDBLOCK; - } else { - rv = SPDYLAY_ERR_CALLBACK_FAILURE; - } - } - return rv; -} - -/* - * The implementation of spdylay_recv_callback type. Here we read data - * from the network and write them in |buf|. The capacity of |buf| is - * |length| bytes. Returns the number of bytes stored in |buf|. See - * the documentation of spdylay_recv_callback for the details. - */ -static ssize_t recv_callback(spdylay_session *session, - uint8_t *buf, size_t length, int flags, - void *user_data) -{ - (void)session; - (void)flags; - - struct Connection *connection; - ssize_t rv; - connection = (struct Connection*)user_data; - connection->want_io = IO_NONE; - ERR_clear_error(); - rv = SSL_read(connection->ssl, buf, length); - if(rv < 0) { - int err = SSL_get_error(connection->ssl, rv); - if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { - connection->want_io = (err == SSL_ERROR_WANT_READ ? - WANT_READ : WANT_WRITE); - rv = SPDYLAY_ERR_WOULDBLOCK; - } else { - rv = SPDYLAY_ERR_CALLBACK_FAILURE; - } - } else if(rv == 0) { - rv = SPDYLAY_ERR_EOF; - } - return rv; -} - -/* - * The implementation of spdylay_before_ctrl_send_callback type. We - * use this function to get stream ID of the request. This is because - * stream ID is not known when we submit the request - * (spdylay_submit_request). - */ -static void before_ctrl_send_callback(spdylay_session *session, - spdylay_frame_type type, - spdylay_frame *frame, - void *user_data) -{ - (void)user_data; - - if(type == SPDYLAY_SYN_STREAM) { - struct Request *req; - int stream_id = frame->syn_stream.stream_id; - req = spdylay_session_get_stream_user_data(session, stream_id); - if(req && req->stream_id == -1) { - req->stream_id = stream_id; - spdylay_printf("[INFO] Stream ID = %d\n", stream_id); - } - } -} - -static void on_ctrl_send_callback(spdylay_session *session, - spdylay_frame_type type, - spdylay_frame *frame, void *user_data) -{ - (void)user_data; - - char **nv; - const char *name = NULL; - int32_t stream_id; - size_t i; - switch(type) { - case SPDYLAY_SYN_STREAM: - nv = frame->syn_stream.nv; - name = "SYN_STREAM"; - stream_id = frame->syn_stream.stream_id; - break; - default: - break; - } - if(name && spdylay_session_get_stream_user_data(session, stream_id)) { - spdylay_printf("[INFO] C ----------------------------> S (%s)\n", name); - for(i = 0; nv[i]; i += 2) { - spdylay_printf(" %s: %s\n", nv[i], nv[i+1]); - } - } -} - -static void on_ctrl_recv_callback(spdylay_session *session, - spdylay_frame_type type, - spdylay_frame *frame, void *user_data) -{ - (void)user_data; - - struct Request *req; - char **nv; - const char *name = NULL; - int32_t stream_id; - size_t i; - switch(type) { - case SPDYLAY_SYN_REPLY: - nv = frame->syn_reply.nv; - name = "SYN_REPLY"; - stream_id = frame->syn_reply.stream_id; - break; - case SPDYLAY_HEADERS: - nv = frame->headers.nv; - name = "HEADERS"; - stream_id = frame->headers.stream_id; - break; - default: - break; - } - if(!name) { - return; - } - req = spdylay_session_get_stream_user_data(session, stream_id); - if(req) { - check_gzip(req, nv); - spdylay_printf("[INFO] C <---------------------------- S (%s)\n", name); - for(i = 0; nv[i]; i += 2) { - spdylay_printf(" %s: %s\n", nv[i], nv[i+1]); - } - } -} - -/* - * The implementation of spdylay_on_stream_close_callback type. We use - * this function to know the response is fully received. Since we just - * fetch 1 resource in this program, after reception of the response, - * we submit GOAWAY and close the session. - */ -static void on_stream_close_callback(spdylay_session *session, - int32_t stream_id, - spdylay_status_code status_code, - void *user_data) -{ - (void)user_data; - (void)status_code; - struct Request *req; - req = spdylay_session_get_stream_user_data(session, stream_id); - if(req) { - int rv; - rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK); - if(rv != 0) { - diec("spdylay_submit_goaway", rv); - } - } -} - -#define MAX_OUTLEN 4096 - -/* - * The implementation of spdylay_on_data_chunk_recv_callback type. We - * use this function to print the received response body. - */ -static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, - int32_t stream_id, - const uint8_t *data, size_t len, - void *user_data) -{ - (void)user_data; - (void)flags; - - struct Request *req; - req = spdylay_session_get_stream_user_data(session, stream_id); - if(req) { - spdylay_printf("[INFO] C <---------------------------- S (DATA)\n"); - spdylay_printf(" %lu bytes\n", (unsigned long int)len); - if(req->inflater) { - while(len > 0) { - uint8_t out[MAX_OUTLEN]; - size_t outlen = MAX_OUTLEN; - size_t tlen = len; - int rv; - rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen); - if(rv == -1) { - spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR); - break; - } - fwrite(out, 1, outlen, stdout); - data += tlen; - len -= tlen; - } - } else { - /* TODO add support gzip */ - fwrite(data, 1, len, stdout); - - //check if the data is correct - if(strcmp(RESPONSE_BODY, (char *)data) != 0) - killparent(parent, "\nreceived data is not the same"); - } - spdylay_printf("\n"); - } -} - -/* - * Setup callback functions. Spdylay API offers many callback - * functions, but most of them are optional. The send_callback is - * always required. Since we use spdylay_session_recv(), the - * recv_callback is also required. - */ -static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks) -{ - memset(callbacks, 0, sizeof(spdylay_session_callbacks)); - callbacks->send_callback = send_callback; - callbacks->recv_callback = recv_callback; - callbacks->before_ctrl_send_callback = before_ctrl_send_callback; - callbacks->on_ctrl_send_callback = on_ctrl_send_callback; - callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback; - callbacks->on_stream_close_callback = on_stream_close_callback; - callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback; -} - -/* - * Callback function for SSL/TLS NPN. Since this program only supports - * SPDY protocol, if server does not offer SPDY protocol the Spdylay - * library supports, we terminate program. - */ -static int select_next_proto_cb(SSL* ssl, - unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, - void *arg) -{ - (void)ssl; - - int rv; - uint16_t *spdy_proto_version; - /* spdylay_select_next_protocol() selects SPDY protocol version the - Spdylay library supports. */ - rv = spdylay_select_next_protocol(out, outlen, in, inlen); - if(rv <= 0) { - die("Server did not advertise spdy/2 or spdy/3 protocol."); - } - spdy_proto_version = (uint16_t*)arg; - *spdy_proto_version = rv; - return SSL_TLSEXT_ERR_OK; -} - -/* - * Setup SSL context. We pass |spdy_proto_version| to get negotiated - * SPDY protocol version in NPN callback. - */ -static void init_ssl_ctx(SSL_CTX *ssl_ctx, uint16_t *spdy_proto_version) -{ - /* Disable SSLv2 and enable all workarounds for buggy servers */ - SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); - /* Set NPN callback */ - SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, - spdy_proto_version); -} - -static void ssl_handshake(SSL *ssl, int fd) -{ - int rv; - if(SSL_set_fd(ssl, fd) == 0) { - dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL)); - } - ERR_clear_error(); - rv = SSL_connect(ssl); - if(rv <= 0) { - dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL)); - } -} - -/* - * Connects to the host |host| and port |port|. This function returns - * the file descriptor of the client socket. - */ -static int connect_to(const char *host, uint16_t port) -{ - struct addrinfo hints; - int fd = -1; - int rv; - char service[NI_MAXSERV]; - struct addrinfo *res, *rp; - snprintf(service, sizeof(service), "%u", port); - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - rv = getaddrinfo(host, service, &hints, &res); - if(rv != 0) { - dief("getaddrinfo", gai_strerror(rv)); - } - for(rp = res; rp; rp = rp->ai_next) { - fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if(fd == -1) { - continue; - } - while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && - errno == EINTR); - if(rv == 0) { - break; - } - MHD_socket_close_(fd); - fd = -1; - } - freeaddrinfo(res); - return fd; -} - -static void make_non_block(int fd) -{ - int flags, rv; - while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR); - if(flags == -1) { - dief("fcntl", strerror(errno)); - } - while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR); - if(rv == -1) { - dief("fcntl", strerror(errno)); - } -} - -/* - * Setting TCP_NODELAY is not mandatory for the SPDY protocol. - */ -static void set_tcp_nodelay(int fd) -{ - int val = 1; - int rv; - rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); - if(rv == -1) { - dief("setsockopt", strerror(errno)); - } -} - -/* - * Update |pollfd| based on the state of |connection|. - */ -static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) -{ - pollfd->events = 0; - if(spdylay_session_want_read(connection->session) || - connection->want_io == WANT_READ) { - pollfd->events |= POLLIN; - } - if(spdylay_session_want_write(connection->session) || - connection->want_io == WANT_WRITE) { - pollfd->events |= POLLOUT; - } -} - -/* - * Submits the request |req| to the connection |connection|. This - * function does not send packets; just append the request to the - * internal queue in |connection->session|. - */ -static void submit_request(struct Connection *connection, struct Request *req) -{ - int pri = 0; - int rv; - const char *nv[15]; - /* We always use SPDY/3 style header even if the negotiated protocol - version is SPDY/2. The library translates the header name as - necessary. Make sure that the last item is NULL! */ - nv[0] = ":method"; nv[1] = "GET"; - nv[2] = ":path"; nv[3] = req->path; - nv[4] = ":version"; nv[5] = "HTTP/1.1"; - nv[6] = ":scheme"; nv[7] = "https"; - nv[8] = ":host"; nv[9] = req->hostport; - nv[10] = "accept"; nv[11] = "*/*"; - nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION; - nv[14] = NULL; - rv = spdylay_submit_request(connection->session, pri, nv, NULL, req); - if(rv != 0) { - diec("spdylay_submit_request", rv); - } -} - -/* - * Performs the network I/O. - */ -static void exec_io(struct Connection *connection) -{ - int rv; - rv = spdylay_session_recv(connection->session); - if(rv != 0) { - diec("spdylay_session_recv", rv); - } - rv = spdylay_session_send(connection->session); - if(rv != 0) { - diec("spdylay_session_send", rv); - } -} - -static void request_init(struct Request *req, const struct URI *uri) -{ - req->host = strcopy(uri->host, uri->hostlen); - req->port = uri->port; - req->path = strcopy(uri->path, uri->pathlen); - req->hostport = strcopy(uri->hostport, uri->hostportlen); - req->stream_id = -1; - req->inflater = NULL; -} - -static void request_free(struct Request *req) -{ - free(req->host); - free(req->path); - free(req->hostport); - spdylay_gzip_inflate_del(req->inflater); -} - -/* - * Fetches the resource denoted by |uri|. - */ -static void -fetch_uri(const struct URI *uri) -{ - spdylay_session_callbacks callbacks; - int fd; - SSL_CTX *ssl_ctx; - SSL *ssl; - struct Request req; - struct Connection connection; - int rv; - nfds_t npollfds = 1; - struct pollfd pollfds[1]; - uint16_t spdy_proto_version; - - request_init(&req, uri); - - setup_spdylay_callbacks(&callbacks); - - /* Establish connection and setup SSL */ - fd = connect_to(req.host, req.port); - if (-1 == fd) - abort (); - ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - if(ssl_ctx == NULL) { - dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL)); - } - init_ssl_ctx(ssl_ctx, &spdy_proto_version); - ssl = SSL_new(ssl_ctx); - if(ssl == NULL) { - dief("SSL_new", ERR_error_string(ERR_get_error(), NULL)); - } - /* To simplify the program, we perform SSL/TLS handshake in blocking - I/O. */ - ssl_handshake(ssl, fd); - - connection.ssl = ssl; - connection.want_io = IO_NONE; - - /* Here make file descriptor non-block */ - make_non_block(fd); - set_tcp_nodelay(fd); - - spdylay_printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version); - rv = spdylay_session_client_new(&connection.session, spdy_proto_version, - &callbacks, &connection); - if(rv != 0) { - diec("spdylay_session_client_new", rv); - } - - /* Submit the HTTP request to the outbound queue. */ - submit_request(&connection, &req); - - pollfds[0].fd = fd; - ctl_poll(pollfds, &connection); - - /* Event loop */ - while(spdylay_session_want_read(connection.session) || - spdylay_session_want_write(connection.session)) { - int nfds = poll(pollfds, npollfds, -1); - if(nfds == -1) { - dief("poll", strerror(errno)); - } - if(pollfds[0].revents & (POLLIN | POLLOUT)) { - exec_io(&connection); - } - if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { - die("Connection error"); - } - ctl_poll(pollfds, &connection); - } - - /* Resource cleanup */ - spdylay_session_del(connection.session); - SSL_shutdown(ssl); - SSL_free(ssl); - SSL_CTX_free(ssl_ctx); - shutdown(fd, SHUT_WR); - MHD_socket_close_ (fd); - request_free(&req); -} - - -static int -parse_uri(struct URI *res, const char *uri) -{ - /* We only interested in https */ - size_t len, i, offset; - memset(res, 0, sizeof(struct URI)); - len = strlen(uri); - if(len < 9 || memcmp("https://", uri, 8) != 0) { - return -1; - } - offset = 8; - res->host = res->hostport = &uri[offset]; - res->hostlen = 0; - if(uri[offset] == '[') { - /* IPv6 literal address */ - ++offset; - ++res->host; - for(i = offset; i < len; ++i) { - if(uri[i] == ']') { - res->hostlen = i-offset; - offset = i+1; - break; - } - } - } else { - const char delims[] = ":/?#"; - for(i = offset; i < len; ++i) { - if(strchr(delims, uri[i]) != NULL) { - break; - } - } - res->hostlen = i-offset; - offset = i; - } - if(res->hostlen == 0) { - return -1; - } - /* Assuming https */ - res->port = 443; - if(offset < len) { - if(uri[offset] == ':') { - /* port */ - const char delims[] = "/?#"; - int port = 0; - ++offset; - for(i = offset; i < len; ++i) { - if(strchr(delims, uri[i]) != NULL) { - break; - } - if('0' <= uri[i] && uri[i] <= '9') { - port *= 10; - port += uri[i]-'0'; - if(port > 65535) { - return -1; - } - } else { - return -1; - } - } - if(port == 0) { - return -1; - } - offset = i; - res->port = port; - } - } - res->hostportlen = uri+offset-res->host; - for(i = offset; i < len; ++i) { - if(uri[i] == '#') { - break; - } - } - if(i-offset == 0) { - res->path = "/"; - res->pathlen = 1; - } else { - res->path = &uri[offset]; - res->pathlen = i-offset; - } - return 0; -} - - -/***** - * end of code needed to utilize spdylay - */ - - -/***** - * start of code needed to utilize microspdy - */ - -void -new_session_callback (void *cls, - struct SPDY_Session * session) -{ - char ipstr[1024]; - - struct sockaddr *addr; - socklen_t addr_len = SPDY_get_remote_addr(session, &addr); - - if(!addr_len) - { - printf("SPDY_get_remote_addr"); - abort(); - } - - if(strcmp(CLS,cls)!=0) - { - killchild(child,"wrong cls"); - } - - if(AF_INET == addr->sa_family) - { - struct sockaddr_in * addr4 = (struct sockaddr_in *) addr; - if(NULL == inet_ntop(AF_INET, &(addr4->sin_addr), ipstr, sizeof(ipstr))) - { - killchild(child,"inet_ntop"); - } - printf("New connection from: %s:%i\n", ipstr, ntohs(addr4->sin_port)); - - loop = 0; - } -#if HAVE_INET6 - else if(AF_INET6 == addr->sa_family) - { - struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *) addr; - if(NULL == inet_ntop(AF_INET6, &(addr6->sin6_addr), ipstr, sizeof(ipstr))) - { - killchild(child,"inet_ntop"); - } - printf("New connection from: %s:%i\n", ipstr, ntohs(addr6->sin6_port)); - - loop = 0; - } -#endif - else - { - killchild(child,"wrong family"); - } -} - -/***** - * end of code needed to utilize microspdy - */ - -//child process -void -childproc(int parent) -{ - struct URI uri; - struct sigaction act; - int rv; - char *uristr; - - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, 0); - - asprintf(&uristr, "https://127.0.0.1:%i/",port); - - SSL_load_error_strings(); - SSL_library_init(); - - rv = parse_uri(&uri, uristr); - if(rv != 0) { - killparent(parent,"parse_uri failed"); - } - fetch_uri(&uri); -} - -//parent proc -int -parentproc(int child) -{ - int childstatus = 0; - unsigned long long timeoutlong=0; - struct timeval timeout; - int ret; - fd_set read_fd_set; - fd_set write_fd_set; - fd_set except_fd_set; - int maxfd = -1; - struct SPDY_Daemon *daemon; - - SPDY_init(); - - daemon = SPDY_start_daemon(port, - DATA_DIR "cert-and-key.pem", - DATA_DIR "cert-and-key.pem", - &new_session_callback,NULL,NULL,NULL,CLS,SPDY_DAEMON_OPTION_END); - - if(NULL==daemon){ - printf("no daemon\n"); - return 1; - } - - do - { - FD_ZERO(&read_fd_set); - FD_ZERO(&write_fd_set); - FD_ZERO(&except_fd_set); - - ret = SPDY_get_timeout(daemon, &timeoutlong); - if(SPDY_NO == ret || timeoutlong > 1000) - { - timeout.tv_sec = 1; - timeout.tv_usec = 0; - } - else - { - timeout.tv_sec = timeoutlong / 1000; - timeout.tv_usec = (timeoutlong % 1000) * 1000; - } - - maxfd = SPDY_get_fdset (daemon, - &read_fd_set, - &write_fd_set, - &except_fd_set); - - ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); - - switch(ret) { - case -1: - printf("select error: %i\n", errno); - killchild(child, "select error"); - break; - case 0: - - break; - default: - SPDY_run(daemon); - - break; - } - } - while(loop && waitpid(child,&childstatus,WNOHANG) != child); - - SPDY_stop_daemon(daemon); - - SPDY_deinit(); - - if(loop) - return WEXITSTATUS(childstatus); - if(waitpid(child,&childstatus,WNOHANG) == child) - return WEXITSTATUS(childstatus); - - kill(child,SIGKILL); - - waitpid(child,&childstatus,0); - - return 0; -} - -int main() -{ - port = get_port(14123); - parent = getpid(); - - child = fork(); - if (child == -1) - { - fprintf(stderr, "can't fork, error %d\n", errno); - exit(EXIT_FAILURE); - } - - if (child == 0) - { - childproc(parent); - _exit(0); - } - else - { - int ret = parentproc(child); - exit(ret); - } - return 1; -} diff --git a/src/testspdy/test_notls.c b/src/testspdy/test_notls.c @@ -1,973 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file request_response.c - * @brief tests receiving request and sending response. spdycli.c (spdylay) - * code is reused here - * @author Andrey Uzunov - * @author Tatsuhiro Tsujikawa - */ - -#include "platform.h" -#include "microspdy.h" -#include <sys/wait.h> -#include "common.h" - -#define RESPONSE_BODY "<html><body><b>Hi, this is libmicrospdy!</b></body></html>" - -#define CLS "anything" - -pid_t parent; -pid_t child; -char *rcvbuf; -int rcvbuf_c = 0; - -int session_closed_called = 0; - -void -killchild(int pid, char *message) -{ - printf("%s\n",message); - kill(pid, SIGKILL); - exit(1); -} - -void -killparent(int pid, char *message) -{ - printf("%s\n",message); - kill(pid, SIGKILL); - _exit(1); -} - - -/***** - * start of code needed to utilize spdylay - */ - -#include <stdint.h> -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <poll.h> -#include <signal.h> -#include <stdio.h> -#include <assert.h> - -#include <spdylay/spdylay.h> - -enum { - IO_NONE, - WANT_READ, - WANT_WRITE -}; - -struct Connection { - spdylay_session *session; - /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it - needs more output; or IO_NONE. This is necessary because SSL/TLS - re-negotiation is possible at any time. Spdylay API offers - similar functions like spdylay_session_want_read() and - spdylay_session_want_write() but they do not take into account - SSL connection. */ - int want_io; - int fd; -}; - -struct Request { - char *host; - uint16_t port; - /* In this program, path contains query component as well. */ - char *path; - /* This is the concatenation of host and port with ":" in - between. */ - char *hostport; - /* Stream ID for this request. */ - int32_t stream_id; - /* The gzip stream inflater for the compressed response. */ - spdylay_gzip *inflater; -}; - -struct URI { - const char *host; - size_t hostlen; - uint16_t port; - /* In this program, path contains query component as well. */ - const char *path; - size_t pathlen; - const char *hostport; - size_t hostportlen; -}; - -/* - * Returns copy of string |s| with the length |len|. The returned - * string is NULL-terminated. - */ -static char* strcopy(const char *s, size_t len) -{ - char *dst; - dst = malloc(len+1); - if (NULL == dst) - abort (); - memcpy(dst, s, len); - dst[len] = '\0'; - return dst; -} - -/* - * Prints error message |msg| and exit. - */ -static void die(const char *msg) -{ - fprintf(stderr, "FATAL: %s\n", msg); - exit(EXIT_FAILURE); -} - -/* - * Prints error containing the function name |func| and message |msg| - * and exit. - */ -static void dief(const char *func, const char *msg) -{ - fprintf(stderr, "FATAL: %s: %s\n", func, msg); - exit(EXIT_FAILURE); -} - -/* - * Prints error containing the function name |func| and error code - * |error_code| and exit. - */ -static void diec(const char *func, int error_code) -{ - fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code, - spdylay_strerror(error_code)); - exit(EXIT_FAILURE); -} - -/* - * Check response is content-encoding: gzip. We need this because SPDY - * client is required to support gzip. - */ -static void check_gzip(struct Request *req, char **nv) -{ - int gzip = 0; - size_t i; - for(i = 0; nv[i]; i += 2) { - if(strcmp("content-encoding", nv[i]) == 0) { - gzip = strcmp("gzip", nv[i+1]) == 0; - break; - } - } - if(gzip) { - int rv; - if(req->inflater) { - return; - } - rv = spdylay_gzip_inflate_new(&req->inflater); - if(rv != 0) { - die("Can't allocate inflate stream."); - } - } -} - -/* - * The implementation of spdylay_send_callback type. Here we write - * |data| with size |length| to the network and return the number of - * bytes actually written. See the documentation of - * spdylay_send_callback for the details. - */ -static ssize_t send_callback(spdylay_session *session, - const uint8_t *data, size_t length, int flags, - void *user_data) -{ - (void)session; - (void)flags; - - struct Connection *connection; - ssize_t rv; - connection = (struct Connection*)user_data; - connection->want_io = IO_NONE; - - rv = write(connection->fd, - data, - length); - - if (rv < 0) - { - switch(errno) - { - case EAGAIN: - #if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: - #endif - connection->want_io = WANT_WRITE; - rv = SPDYLAY_ERR_WOULDBLOCK; - break; - - default: - rv = SPDYLAY_ERR_CALLBACK_FAILURE; - } - } - return rv; -} - -/* - * The implementation of spdylay_recv_callback type. Here we read data - * from the network and write them in |buf|. The capacity of |buf| is - * |length| bytes. Returns the number of bytes stored in |buf|. See - * the documentation of spdylay_recv_callback for the details. - */ -static ssize_t recv_callback(spdylay_session *session, - uint8_t *buf, size_t length, int flags, - void *user_data) -{ - (void)session; - (void)flags; - - struct Connection *connection; - ssize_t rv; - connection = (struct Connection*)user_data; - connection->want_io = IO_NONE; - - rv = read(connection->fd, - buf, - length); - - if (rv < 0) - { - switch(errno) - { - case EAGAIN: - #if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: - #endif - connection->want_io = WANT_READ; - rv = SPDYLAY_ERR_WOULDBLOCK; - break; - - default: - rv = SPDYLAY_ERR_CALLBACK_FAILURE; - } - } - else if(rv == 0) - rv = SPDYLAY_ERR_EOF; - return rv; -} - -/* - * The implementation of spdylay_before_ctrl_send_callback type. We - * use this function to get stream ID of the request. This is because - * stream ID is not known when we submit the request - * (spdylay_submit_request). - */ -static void before_ctrl_send_callback(spdylay_session *session, - spdylay_frame_type type, - spdylay_frame *frame, - void *user_data) -{ - (void)user_data; - - if(type == SPDYLAY_SYN_STREAM) { - struct Request *req; - int stream_id = frame->syn_stream.stream_id; - req = spdylay_session_get_stream_user_data(session, stream_id); - if(req && req->stream_id == -1) { - req->stream_id = stream_id; - printf("[INFO] Stream ID = %d\n", stream_id); - } - } -} - -static void on_ctrl_send_callback(spdylay_session *session, - spdylay_frame_type type, - spdylay_frame *frame, void *user_data) -{ - (void)user_data; - - char **nv; - const char *name = NULL; - int32_t stream_id; - size_t i; - switch(type) { - case SPDYLAY_SYN_STREAM: - nv = frame->syn_stream.nv; - name = "SYN_STREAM"; - stream_id = frame->syn_stream.stream_id; - break; - default: - break; - } - if(name && spdylay_session_get_stream_user_data(session, stream_id)) { - printf("[INFO] C ----------------------------> S (%s)\n", name); - for(i = 0; nv[i]; i += 2) { - printf(" %s: %s\n", nv[i], nv[i+1]); - } - } -} - -static void on_ctrl_recv_callback(spdylay_session *session, - spdylay_frame_type type, - spdylay_frame *frame, void *user_data) -{ - (void)user_data; - - struct Request *req; - char **nv; - const char *name = NULL; - int32_t stream_id; - size_t i; - switch(type) { - case SPDYLAY_SYN_REPLY: - nv = frame->syn_reply.nv; - name = "SYN_REPLY"; - stream_id = frame->syn_reply.stream_id; - break; - case SPDYLAY_HEADERS: - nv = frame->headers.nv; - name = "HEADERS"; - stream_id = frame->headers.stream_id; - break; - default: - break; - } - if(!name) { - return; - } - req = spdylay_session_get_stream_user_data(session, stream_id); - if(req) { - check_gzip(req, nv); - printf("[INFO] C <---------------------------- S (%s)\n", name); - for(i = 0; nv[i]; i += 2) { - printf(" %s: %s\n", nv[i], nv[i+1]); - } - } -} - -/* - * The implementation of spdylay_on_stream_close_callback type. We use - * this function to know the response is fully received. Since we just - * fetch 1 resource in this program, after reception of the response, - * we submit GOAWAY and close the session. - */ -static void on_stream_close_callback(spdylay_session *session, - int32_t stream_id, - spdylay_status_code status_code, - void *user_data) -{ - (void)status_code; - (void)user_data; - - struct Request *req; - req = spdylay_session_get_stream_user_data(session, stream_id); - if(req) { - int rv; - rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK); - if(rv != 0) { - diec("spdylay_submit_goaway", rv); - } - } -} - -#define MAX_OUTLEN 4096 - -/* - * The implementation of spdylay_on_data_chunk_recv_callback type. We - * use this function to print the received response body. - */ -static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, - int32_t stream_id, - const uint8_t *data, size_t len, - void *user_data) -{ - (void)flags; - (void)user_data; - - struct Request *req; - req = spdylay_session_get_stream_user_data(session, stream_id); - if(req) { - printf("[INFO] C <---------------------------- S (DATA)\n"); - printf(" %lu bytes\n", (unsigned long int)len); - if(req->inflater) { - while(len > 0) { - uint8_t out[MAX_OUTLEN]; - size_t outlen = MAX_OUTLEN; - size_t tlen = len; - int rv; - rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen); - if(rv == -1) { - spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR); - break; - } - fwrite(out, 1, outlen, stdout); - data += tlen; - len -= tlen; - } - } else { - /* TODO add support gzip */ - fwrite(data, 1, len, stdout); - - //check if the data is correct - //if(strcmp(RESPONSE_BODY, data) != 0) - //killparent(parent, "\nreceived data is not the same"); - if(len + rcvbuf_c > strlen(RESPONSE_BODY)) - killparent(parent, "\nreceived data is not the same"); - - strcpy(rcvbuf + rcvbuf_c,(char*)data); - rcvbuf_c+=len; - } - printf("\n"); - } -} - -/* - * Setup callback functions. Spdylay API offers many callback - * functions, but most of them are optional. The send_callback is - * always required. Since we use spdylay_session_recv(), the - * recv_callback is also required. - */ -static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks) -{ - memset(callbacks, 0, sizeof(spdylay_session_callbacks)); - callbacks->send_callback = send_callback; - callbacks->recv_callback = recv_callback; - callbacks->before_ctrl_send_callback = before_ctrl_send_callback; - callbacks->on_ctrl_send_callback = on_ctrl_send_callback; - callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback; - callbacks->on_stream_close_callback = on_stream_close_callback; - callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback; -} - - -/* - * Connects to the host |host| and port |port|. This function returns - * the file descriptor of the client socket. - */ -static int connect_to(const char *host, uint16_t port) -{ - struct addrinfo hints; - int fd = -1; - int rv; - char service[NI_MAXSERV]; - struct addrinfo *res, *rp; - snprintf(service, sizeof(service), "%u", port); - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - rv = getaddrinfo(host, service, &hints, &res); - if(rv != 0) { - dief("getaddrinfo", gai_strerror(rv)); - } - for(rp = res; rp; rp = rp->ai_next) { - fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if(fd == -1) { - continue; - } - while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && - errno == EINTR); - if(rv == 0) { - break; - } - MHD_socket_close_(fd); - fd = -1; - dief("connect", strerror(errno)); - } - freeaddrinfo(res); - return fd; -} - -static void make_non_block(int fd) -{ - int flags, rv; - while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR); - if(flags == -1) { - dief("fcntl1", strerror(errno)); - } - while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR); - if(rv == -1) { - dief("fcntl2", strerror(errno)); - } -} - -/* - * Setting TCP_NODELAY is not mandatory for the SPDY protocol. - */ -static void set_tcp_nodelay(int fd) -{ - int val = 1; - int rv; - rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); - if(rv == -1) { - dief("setsockopt", strerror(errno)); - } -} - -/* - * Update |pollfd| based on the state of |connection|. - */ -static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) -{ - pollfd->events = 0; - if(spdylay_session_want_read(connection->session) || - connection->want_io == WANT_READ) { - pollfd->events |= POLLIN; - } - if(spdylay_session_want_write(connection->session) || - connection->want_io == WANT_WRITE) { - pollfd->events |= POLLOUT; - } -} - -/* - * Submits the request |req| to the connection |connection|. This - * function does not send packets; just append the request to the - * internal queue in |connection->session|. - */ -static void submit_request(struct Connection *connection, struct Request *req) -{ - int pri = 0; - int rv; - const char *nv[15]; - /* We always use SPDY/3 style header even if the negotiated protocol - version is SPDY/2. The library translates the header name as - necessary. Make sure that the last item is NULL! */ - nv[0] = ":method"; nv[1] = "GET"; - nv[2] = ":path"; nv[3] = req->path; - nv[4] = ":version"; nv[5] = "HTTP/1.1"; - nv[6] = ":scheme"; nv[7] = "https"; - nv[8] = ":host"; nv[9] = req->hostport; - nv[10] = "accept"; nv[11] = "*/*"; - nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION; - nv[14] = NULL; - rv = spdylay_submit_request(connection->session, pri, nv, NULL, req); - if(rv != 0) { - diec("spdylay_submit_request", rv); - } -} - -/* - * Performs the network I/O. - */ -static void exec_io(struct Connection *connection) -{ - int rv; - rv = spdylay_session_recv(connection->session); - if(rv != 0) { - diec("spdylay_session_recv", rv); - } - rv = spdylay_session_send(connection->session); - if(rv != 0) { - diec("spdylay_session_send", rv); - } -} - -static void request_init(struct Request *req, const struct URI *uri) -{ - req->host = strcopy(uri->host, uri->hostlen); - req->port = uri->port; - req->path = strcopy(uri->path, uri->pathlen); - req->hostport = strcopy(uri->hostport, uri->hostportlen); - req->stream_id = -1; - req->inflater = NULL; -} - -static void request_free(struct Request *req) -{ - free(req->host); - free(req->path); - free(req->hostport); - spdylay_gzip_inflate_del(req->inflater); -} - -/* - * Fetches the resource denoted by |uri|. - */ -static void fetch_uri(const struct URI *uri) -{ - spdylay_session_callbacks callbacks; - int fd; - struct Request req; - struct Connection connection; - int rv; - nfds_t npollfds = 1; - struct pollfd pollfds[1]; - uint16_t spdy_proto_version = 3; - - request_init(&req, uri); - - setup_spdylay_callbacks(&callbacks); - - /* Establish connection and setup SSL */ - fd = connect_to(req.host, req.port); - if (-1 == fd) - abort (); - - connection.fd = fd; - connection.want_io = IO_NONE; - - /* Here make file descriptor non-block */ - make_non_block(fd); - set_tcp_nodelay(fd); - - printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version); - rv = spdylay_session_client_new(&connection.session, spdy_proto_version, - &callbacks, &connection); - if(rv != 0) { - diec("spdylay_session_client_new", rv); - } - - /* Submit the HTTP request to the outbound queue. */ - submit_request(&connection, &req); - - pollfds[0].fd = fd; - ctl_poll(pollfds, &connection); - - /* Event loop */ - while(spdylay_session_want_read(connection.session) || - spdylay_session_want_write(connection.session)) { - int nfds = poll(pollfds, npollfds, -1); - if(nfds == -1) { - dief("poll", strerror(errno)); - } - if(pollfds[0].revents & (POLLIN | POLLOUT)) { - exec_io(&connection); - } - if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { - die("Connection error"); - } - ctl_poll(pollfds, &connection); - } - - /* Resource cleanup */ - spdylay_session_del(connection.session); - shutdown(fd, SHUT_WR); - MHD_socket_close_(fd); - request_free(&req); -} - -static int parse_uri(struct URI *res, const char *uri) -{ - /* We only interested in https */ - size_t len, i, offset; - memset(res, 0, sizeof(struct URI)); - len = strlen(uri); - if(len < 9 || memcmp("https://", uri, 8) != 0) { - return -1; - } - offset = 8; - res->host = res->hostport = &uri[offset]; - res->hostlen = 0; - if(uri[offset] == '[') { - /* IPv6 literal address */ - ++offset; - ++res->host; - for(i = offset; i < len; ++i) { - if(uri[i] == ']') { - res->hostlen = i-offset; - offset = i+1; - break; - } - } - } else { - const char delims[] = ":/?#"; - for(i = offset; i < len; ++i) { - if(strchr(delims, uri[i]) != NULL) { - break; - } - } - res->hostlen = i-offset; - offset = i; - } - if(res->hostlen == 0) { - return -1; - } - /* Assuming https */ - res->port = 443; - if(offset < len) { - if(uri[offset] == ':') { - /* port */ - const char delims[] = "/?#"; - int port = 0; - ++offset; - for(i = offset; i < len; ++i) { - if(strchr(delims, uri[i]) != NULL) { - break; - } - if('0' <= uri[i] && uri[i] <= '9') { - port *= 10; - port += uri[i]-'0'; - if(port > 65535) { - return -1; - } - } else { - return -1; - } - } - if(port == 0) { - return -1; - } - offset = i; - res->port = port; - } - } - res->hostportlen = uri+offset-res->host; - for(i = offset; i < len; ++i) { - if(uri[i] == '#') { - break; - } - } - if(i-offset == 0) { - res->path = "/"; - res->pathlen = 1; - } else { - res->path = &uri[offset]; - res->pathlen = i-offset; - } - return 0; -} - - -/***** - * end of code needed to utilize spdylay - */ - - -/***** - * start of code needed to utilize microspdy - */ - - -void -standard_request_handler(void *cls, - struct SPDY_Request * request, - uint8_t priority, - const char *method, - const char *path, - const char *version, - const char *host, - const char *scheme, - struct SPDY_NameValue * headers, - bool more) -{ - (void)cls; - (void)request; - (void)priority; - (void)host; - (void)scheme; - (void)headers; - (void)method; - (void)version; - (void)more; - - struct SPDY_Response *response=NULL; - - if(strcmp(CLS,cls)!=0) - { - killchild(child,"wrong cls"); - } - - response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,RESPONSE_BODY,strlen(RESPONSE_BODY)); - - if(NULL==response){ - fprintf(stdout,"no response obj\n"); - exit(3); - } - - if(SPDY_queue_response(request,response,true,false,NULL,(void*)strdup(path))!=SPDY_YES) - { - fprintf(stdout,"queue\n"); - exit(4); - } -} - -void -session_closed_handler (void *cls, - struct SPDY_Session * session, - int by_client) -{ - printf("session_closed_handler called\n"); - - if(strcmp(CLS,cls)!=0) - { - killchild(child,"wrong cls"); - } - - if(SPDY_YES != by_client) - { - //killchild(child,"wrong by_client"); - printf("session closed by server\n"); - } - else - { - printf("session closed by client\n"); - } - - if(NULL == session) - { - killchild(child,"session is NULL"); - } - - session_closed_called = 1; -} - - -/***** - * end of code needed to utilize microspdy - */ - -//child process -void -childproc(int port) -{ - struct URI uri; - struct sigaction act; - int rv; - char *uristr; - - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, 0); - - usleep(10000); - asprintf(&uristr, "https://127.0.0.1:%i/",port); - if(NULL == (rcvbuf = malloc(strlen(RESPONSE_BODY)+1))) - killparent(parent,"no memory"); - - rv = parse_uri(&uri, uristr); - if(rv != 0) { - killparent(parent,"parse_uri failed"); - } - fetch_uri(&uri); - - if(strcmp(rcvbuf, RESPONSE_BODY)) - killparent(parent,"received data is different"); -} - -//parent proc -int -parentproc( int port) -{ - int childstatus; - unsigned long long timeoutlong=0; - struct timeval timeout; - int ret; - fd_set read_fd_set; - fd_set write_fd_set; - fd_set except_fd_set; - int maxfd = -1; - struct SPDY_Daemon *daemon; - - SPDY_init(); - - daemon = SPDY_start_daemon(port, - NULL, - NULL, - NULL,&session_closed_handler,&standard_request_handler,NULL,CLS, - SPDY_DAEMON_OPTION_IO_SUBSYSTEM, SPDY_IO_SUBSYSTEM_RAW, - SPDY_DAEMON_OPTION_FLAGS, SPDY_DAEMON_FLAG_NO_DELAY, - SPDY_DAEMON_OPTION_END); - - if(NULL==daemon){ - printf("no daemon\n"); - return 1; - } - - do - { - FD_ZERO(&read_fd_set); - FD_ZERO(&write_fd_set); - FD_ZERO(&except_fd_set); - - ret = SPDY_get_timeout(daemon, &timeoutlong); - if(SPDY_NO == ret || timeoutlong > 1000) - { - timeout.tv_sec = 1; - timeout.tv_usec = 0; - } - else - { - timeout.tv_sec = timeoutlong / 1000; - timeout.tv_usec = (timeoutlong % 1000) * 1000; - } - - maxfd = SPDY_get_fdset (daemon, - &read_fd_set, - &write_fd_set, - &except_fd_set); - - ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); - - switch(ret) { - case -1: - printf("select error: %i\n", errno); - killchild(child, "select error"); - break; - case 0: - - break; - default: - SPDY_run(daemon); - - break; - } - } - while(waitpid(child,&childstatus,WNOHANG) != child); - - //give chance to the client to close socket and handle this in run - usleep(100000); - SPDY_run(daemon); - - SPDY_stop_daemon(daemon); - - SPDY_deinit(); - - return WEXITSTATUS(childstatus); -} - -int main() -{ - int port = get_port(12123); - parent = getpid(); - - child = fork(); - if (child == -1) - { - fprintf(stderr, "can't fork, error %d\n", errno); - exit(EXIT_FAILURE); - } - - if (child == 0) - { - childproc(port); - _exit(0); - } - else - { - int ret = parentproc(port); - if(1 == session_closed_called && 0 == ret) - exit(0); - else - exit(ret ? ret : 21); - } - return 1; -} diff --git a/src/testspdy/test_proxies.c b/src/testspdy/test_proxies.c @@ -1,255 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file test_proxies.c - * @brief test curl > mhd2spdylay > microspdy2http > mhd - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "microspdy.h" -#include "common.h" -#include <sys/wait.h> -#include <stdio.h> /* printf, stderr, fprintf */ -#include <sys/types.h> /* pid_t */ -#include <unistd.h> /* _exit, fork */ -#include <stdlib.h> /* exit */ -#include <errno.h> /* errno */ -#include <sys/wait.h> /* pid_t */ -#include "common.h" - -#ifdef _WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN 1 -#endif /* !WIN32_LEAN_AND_MEAN */ -#include <windows.h> -#endif - -#define EXPECTED_BODY "<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>" - - -pid_t parent; - pid_t child_mhd; - pid_t child_spdy2http; - pid_t child_mhd2spdy; - pid_t child_curl; - - uint16_t mhd_port; - uint16_t spdy2http_port; - uint16_t mhd2spdy_port; - -void -killproc(int pid, const char *message) -{ - printf("%s\nkilling %i\n",message,pid); - kill(pid, SIGKILL); -} - - -void killchildren() -{ - if(0 != child_mhd) - killproc(child_mhd,"kill mhd\n"); - if(0 != child_spdy2http) - killproc(child_spdy2http,"kill spdy2http\n"); - if(0 != child_mhd2spdy) - killproc(child_mhd2spdy,"kill mhd2spdy\n"); - if(0 != child_curl) - killproc(child_curl,"kill curl\n"); -} - -pid_t au_fork() -{ - pid_t child = fork(); - if (child == -1) - { - killchildren(); - - killproc(parent,"fork failed\n"); - } - - return child; -} - - -int main() -{ - //pid_t child; - int childstatus; - pid_t wpid; - - parent = getpid(); - mhd_port = get_port(4000); - spdy2http_port = get_port(4100); - mhd2spdy_port = get_port(4200); - - child_mhd = au_fork(); - if (child_mhd == 0) - { - //run MHD - pid_t devnull; - char *port_s; - - close(1); - devnull = open("/dev/null", O_WRONLY); - if (-1 == devnull) - abort(); - if (1 != devnull) - { - dup2(devnull, 1); - close(devnull); - } - asprintf(&port_s, "%i", mhd_port); - execlp ("../examples/minimal_example", "minimal_example", port_s, NULL); - fprintf(stderr, "executing mhd failed\nFor this test 'make' must be run before 'make check'!\n"); - //killchildren(); - _exit(1); - } - - - child_spdy2http = au_fork(); - if (child_spdy2http == 0) - { - //run spdy2http - pid_t devnull; - char *port_s; - //char *url; - - close(1); - devnull = open("/dev/null", O_WRONLY); - if (-1 == devnull) - abort(); - if (1 != devnull) - { - dup2(devnull, 1); - close(devnull); - } - //asprintf(&url, "127.0.0.1:%i", mhd_port); - asprintf(&port_s, "%i", spdy2http_port); - sleep(1); - execlp ("../spdy2http/microspdy2http", "microspdy2http", "-v4rtT", "10", "-p", port_s, NULL); - fprintf(stderr, "executing microspdy2http failed\n"); - //killchildren(); - _exit(1); - } - - child_mhd2spdy = au_fork(); - if (child_mhd2spdy == 0) - { - //run MHD2sdpy - pid_t devnull; - char *port_s; - char *url; - - close(1); - devnull = open("/dev/null", O_WRONLY); - if (-1 == devnull) - abort(); - if (1 != devnull) - { - dup2(devnull, 1); - close(devnull); - } - asprintf(&url, "http://127.0.0.1:%i", spdy2http_port); - asprintf(&port_s, "%i", mhd2spdy_port); - sleep(2); - execlp ("../examples/mhd2spdy", "mhd2spdy", "-vosb", url, "-p", port_s, NULL); - fprintf(stderr, "executing mhd2spdy failed\n"); - //killchildren(); - _exit(1); - } - - child_curl = au_fork(); - if (child_curl == 0) - { - //run curl - FILE *p; - pid_t devnull; - char *cmd; - unsigned int i; - int retc; - char buf[strlen(EXPECTED_BODY) + 1]; - - close(1); - devnull = open("/dev/null", O_WRONLY); - if (-1 == devnull) - abort (); - if (1 != devnull) - { - dup2(devnull, 1); - close(devnull); - } - - asprintf (&cmd, "curl --proxy http://127.0.0.1:%i http://127.0.0.1:%i/", mhd2spdy_port, mhd_port); - sleep(3); - p = popen(cmd, "r"); - if (p != NULL) - { - for (i = 0; i < strlen(EXPECTED_BODY) && !feof(p); i++) - { - retc = fgetc (p); - if (EOF == retc) - abort (); /* what did feof(p) do there!? */ - buf[i] = (char) retc; - } - - pclose(p); - buf[i] = 0; - _exit(strcmp(EXPECTED_BODY, buf)); - } - fprintf(stderr, "executing curl failed\n"); - //killchildren(); - _exit(1); - } - - do - { - wpid = waitpid(child_mhd,&childstatus,WNOHANG); - if(wpid == child_mhd) - { - fprintf(stderr, "mhd died unexpectedly\n"); - killchildren(); - return 1; - } - - wpid = waitpid(child_spdy2http,&childstatus,WNOHANG); - if(wpid == child_spdy2http) - { - fprintf(stderr, "spdy2http died unexpectedly\n"); - killchildren(); - return 1; - } - - wpid = waitpid(child_mhd2spdy,&childstatus,WNOHANG); - if(wpid == child_mhd2spdy) - { - fprintf(stderr, "mhd2spdy died unexpectedly\n"); - killchildren(); - return 1; - } - - if(waitpid(child_curl,&childstatus,WNOHANG) == child_curl) - { - killchildren(); - return WEXITSTATUS(childstatus); - } - sleep(1); - } - while(true); -} diff --git a/src/testspdy/test_request_response.c b/src/testspdy/test_request_response.c @@ -1,1029 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file test_request_response.c - * @brief tests receiving request and sending response. spdycli.c (spdylay) - * code is reused here - * @author Andrey Uzunov - * @author Tatsuhiro Tsujikawa - */ - -#include "platform.h" -#include "microspdy.h" -#include <sys/wait.h> -#include "common.h" - -#define RESPONSE_BODY "<html><body><b>Hi, this is libmicrospdy!</b></body></html>" - -#define CLS "anything" - -pid_t parent; -pid_t child; -char *rcvbuf; -int rcvbuf_c = 0; - -int session_closed_called = 0; - -void -killchild(int pid, char *message) -{ - printf("%s\n",message); - kill(pid, SIGKILL); - exit(1); -} - -void -killparent(int pid, char *message) -{ - printf("%s\n",message); - kill(pid, SIGKILL); - _exit(1); -} - - -/***** - * start of code needed to utilize spdylay - */ - -#include <stdint.h> -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <poll.h> -#include <signal.h> -#include <stdio.h> -#include <assert.h> - -#include <spdylay/spdylay.h> - -#include <openssl/ssl.h> -#include <openssl/err.h> - -enum { - IO_NONE, - WANT_READ, - WANT_WRITE -}; - -struct Connection { - SSL *ssl; - spdylay_session *session; - /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it - needs more output; or IO_NONE. This is necessary because SSL/TLS - re-negotiation is possible at any time. Spdylay API offers - similar functions like spdylay_session_want_read() and - spdylay_session_want_write() but they do not take into account - SSL connection. */ - int want_io; -}; - -struct Request { - char *host; - uint16_t port; - /* In this program, path contains query component as well. */ - char *path; - /* This is the concatenation of host and port with ":" in - between. */ - char *hostport; - /* Stream ID for this request. */ - int32_t stream_id; - /* The gzip stream inflater for the compressed response. */ - spdylay_gzip *inflater; -}; - -struct URI { - const char *host; - size_t hostlen; - uint16_t port; - /* In this program, path contains query component as well. */ - const char *path; - size_t pathlen; - const char *hostport; - size_t hostportlen; -}; - -/* - * Returns copy of string |s| with the length |len|. The returned - * string is NULL-terminated. - */ -static char* strcopy(const char *s, size_t len) -{ - char *dst; - dst = malloc(len+1); - if (NULL == dst) - abort (); - memcpy(dst, s, len); - dst[len] = '\0'; - return dst; -} - -/* - * Prints error message |msg| and exit. - */ -static void die(const char *msg) -{ - fprintf(stderr, "FATAL: %s\n", msg); - exit(EXIT_FAILURE); -} - -/* - * Prints error containing the function name |func| and message |msg| - * and exit. - */ -static void dief(const char *func, const char *msg) -{ - fprintf(stderr, "FATAL: %s: %s\n", func, msg); - exit(EXIT_FAILURE); -} - -/* - * Prints error containing the function name |func| and error code - * |error_code| and exit. - */ -static void diec(const char *func, int error_code) -{ - fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code, - spdylay_strerror(error_code)); - exit(EXIT_FAILURE); -} - -/* - * Check response is content-encoding: gzip. We need this because SPDY - * client is required to support gzip. - */ -static void check_gzip(struct Request *req, char **nv) -{ - int gzip = 0; - size_t i; - for(i = 0; nv[i]; i += 2) { - if(strcmp("content-encoding", nv[i]) == 0) { - gzip = strcmp("gzip", nv[i+1]) == 0; - break; - } - } - if(gzip) { - int rv; - if(req->inflater) { - return; - } - rv = spdylay_gzip_inflate_new(&req->inflater); - if(rv != 0) { - die("Can't allocate inflate stream."); - } - } -} - -/* - * The implementation of spdylay_send_callback type. Here we write - * |data| with size |length| to the network and return the number of - * bytes actually written. See the documentation of - * spdylay_send_callback for the details. - */ -static ssize_t send_callback(spdylay_session *session, - const uint8_t *data, size_t length, int flags, - void *user_data) -{ - (void)session; - (void)flags; - - struct Connection *connection; - ssize_t rv; - connection = (struct Connection*)user_data; - connection->want_io = IO_NONE; - ERR_clear_error(); - rv = SSL_write(connection->ssl, data, length); - if(rv < 0) { - int err = SSL_get_error(connection->ssl, rv); - if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { - connection->want_io = (err == SSL_ERROR_WANT_READ ? - WANT_READ : WANT_WRITE); - rv = SPDYLAY_ERR_WOULDBLOCK; - } else { - rv = SPDYLAY_ERR_CALLBACK_FAILURE; - } - } - return rv; -} - -/* - * The implementation of spdylay_recv_callback type. Here we read data - * from the network and write them in |buf|. The capacity of |buf| is - * |length| bytes. Returns the number of bytes stored in |buf|. See - * the documentation of spdylay_recv_callback for the details. - */ -static ssize_t recv_callback(spdylay_session *session, - uint8_t *buf, size_t length, int flags, - void *user_data) -{ - (void)session; - (void)flags; - - struct Connection *connection; - ssize_t rv; - connection = (struct Connection*)user_data; - connection->want_io = IO_NONE; - ERR_clear_error(); - rv = SSL_read(connection->ssl, buf, length); - if(rv < 0) { - int err = SSL_get_error(connection->ssl, rv); - if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { - connection->want_io = (err == SSL_ERROR_WANT_READ ? - WANT_READ : WANT_WRITE); - rv = SPDYLAY_ERR_WOULDBLOCK; - } else { - rv = SPDYLAY_ERR_CALLBACK_FAILURE; - } - } else if(rv == 0) { - rv = SPDYLAY_ERR_EOF; - } - return rv; -} - -/* - * The implementation of spdylay_before_ctrl_send_callback type. We - * use this function to get stream ID of the request. This is because - * stream ID is not known when we submit the request - * (spdylay_submit_request). - */ -static void before_ctrl_send_callback(spdylay_session *session, - spdylay_frame_type type, - spdylay_frame *frame, - void *user_data) -{ - (void)user_data; - - if(type == SPDYLAY_SYN_STREAM) { - struct Request *req; - int stream_id = frame->syn_stream.stream_id; - req = spdylay_session_get_stream_user_data(session, stream_id); - if(req && req->stream_id == -1) { - req->stream_id = stream_id; - printf("[INFO] Stream ID = %d\n", stream_id); - } - } -} - -static void on_ctrl_send_callback(spdylay_session *session, - spdylay_frame_type type, - spdylay_frame *frame, void *user_data) -{ - (void)user_data; - - char **nv; - const char *name = NULL; - int32_t stream_id; - size_t i; - switch(type) { - case SPDYLAY_SYN_STREAM: - nv = frame->syn_stream.nv; - name = "SYN_STREAM"; - stream_id = frame->syn_stream.stream_id; - break; - default: - break; - } - if(name && spdylay_session_get_stream_user_data(session, stream_id)) { - printf("[INFO] C ----------------------------> S (%s)\n", name); - for(i = 0; nv[i]; i += 2) { - printf(" %s: %s\n", nv[i], nv[i+1]); - } - } -} - -static void on_ctrl_recv_callback(spdylay_session *session, - spdylay_frame_type type, - spdylay_frame *frame, void *user_data) -{ - (void)user_data; - - struct Request *req; - char **nv; - const char *name = NULL; - int32_t stream_id; - size_t i; - switch(type) { - case SPDYLAY_SYN_REPLY: - nv = frame->syn_reply.nv; - name = "SYN_REPLY"; - stream_id = frame->syn_reply.stream_id; - break; - case SPDYLAY_HEADERS: - nv = frame->headers.nv; - name = "HEADERS"; - stream_id = frame->headers.stream_id; - break; - default: - break; - } - if(!name) { - return; - } - req = spdylay_session_get_stream_user_data(session, stream_id); - if(req) { - check_gzip(req, nv); - printf("[INFO] C <---------------------------- S (%s)\n", name); - for(i = 0; nv[i]; i += 2) { - printf(" %s: %s\n", nv[i], nv[i+1]); - } - } -} - -/* - * The implementation of spdylay_on_stream_close_callback type. We use - * this function to know the response is fully received. Since we just - * fetch 1 resource in this program, after reception of the response, - * we submit GOAWAY and close the session. - */ -static void on_stream_close_callback(spdylay_session *session, - int32_t stream_id, - spdylay_status_code status_code, - void *user_data) -{ - (void)user_data; - (void)status_code; - - struct Request *req; - req = spdylay_session_get_stream_user_data(session, stream_id); - if(req) { - int rv; - rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK); - if(rv != 0) { - diec("spdylay_submit_goaway", rv); - } - } -} - -#define MAX_OUTLEN 4096 - -/* - * The implementation of spdylay_on_data_chunk_recv_callback type. We - * use this function to print the received response body. - */ -static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, - int32_t stream_id, - const uint8_t *data, size_t len, - void *user_data) -{ - (void)user_data; - (void)flags; - - struct Request *req; - req = spdylay_session_get_stream_user_data(session, stream_id); - if(req) { - printf("[INFO] C <---------------------------- S (DATA)\n"); - printf(" %lu bytes\n", (unsigned long int)len); - if(req->inflater) { - while(len > 0) { - uint8_t out[MAX_OUTLEN]; - size_t outlen = MAX_OUTLEN; - size_t tlen = len; - int rv; - rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen); - if(rv == -1) { - spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR); - break; - } - fwrite(out, 1, outlen, stdout); - data += tlen; - len -= tlen; - } - } else { - /* TODO add support gzip */ - fwrite(data, 1, len, stdout); - - //check if the data is correct - //if(strcmp(RESPONSE_BODY, data) != 0) - //killparent(parent, "\nreceived data is not the same"); - if(len + rcvbuf_c > strlen(RESPONSE_BODY)) - killparent(parent, "\nreceived data is not the same"); - - strcpy(rcvbuf + rcvbuf_c,(char*)data); - rcvbuf_c+=len; - } - printf("\n"); - } -} - -/* - * Setup callback functions. Spdylay API offers many callback - * functions, but most of them are optional. The send_callback is - * always required. Since we use spdylay_session_recv(), the - * recv_callback is also required. - */ -static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks) -{ - memset(callbacks, 0, sizeof(spdylay_session_callbacks)); - callbacks->send_callback = send_callback; - callbacks->recv_callback = recv_callback; - callbacks->before_ctrl_send_callback = before_ctrl_send_callback; - callbacks->on_ctrl_send_callback = on_ctrl_send_callback; - callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback; - callbacks->on_stream_close_callback = on_stream_close_callback; - callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback; -} - -/* - * Callback function for SSL/TLS NPN. Since this program only supports - * SPDY protocol, if server does not offer SPDY protocol the Spdylay - * library supports, we terminate program. - */ -static int select_next_proto_cb(SSL* ssl, - unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, - void *arg) -{ - (void)ssl; - - int rv; - uint16_t *spdy_proto_version; - /* spdylay_select_next_protocol() selects SPDY protocol version the - Spdylay library supports. */ - rv = spdylay_select_next_protocol(out, outlen, in, inlen); - if(rv <= 0) { - die("Server did not advertise spdy/2 or spdy/3 protocol."); - } - spdy_proto_version = (uint16_t*)arg; - *spdy_proto_version = rv; - return SSL_TLSEXT_ERR_OK; -} - -/* - * Setup SSL context. We pass |spdy_proto_version| to get negotiated - * SPDY protocol version in NPN callback. - */ -static void init_ssl_ctx(SSL_CTX *ssl_ctx, uint16_t *spdy_proto_version) -{ - /* Disable SSLv2 and enable all workarounds for buggy servers */ - SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); - /* Set NPN callback */ - SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, - spdy_proto_version); -} - -static void ssl_handshake(SSL *ssl, int fd) -{ - int rv; - if(SSL_set_fd(ssl, fd) == 0) { - dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL)); - } - ERR_clear_error(); - rv = SSL_connect(ssl); - if(rv <= 0) { - dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL)); - } -} - -/* - * Connects to the host |host| and port |port|. This function returns - * the file descriptor of the client socket. - */ -static int connect_to(const char *host, uint16_t port) -{ - struct addrinfo hints; - int fd = -1; - int rv; - char service[NI_MAXSERV]; - struct addrinfo *res, *rp; - snprintf(service, sizeof(service), "%u", port); - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - rv = getaddrinfo(host, service, &hints, &res); - if(rv != 0) { - dief("getaddrinfo", gai_strerror(rv)); - } - for(rp = res; rp; rp = rp->ai_next) { - fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if(fd == -1) { - continue; - } - while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && - errno == EINTR); - if(rv == 0) { - break; - } - MHD_socket_close_(fd); - fd = -1; - } - freeaddrinfo(res); - return fd; -} - -static void make_non_block(int fd) -{ - int flags, rv; - while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR); - if(flags == -1) { - dief("fcntl", strerror(errno)); - } - while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR); - if(rv == -1) { - dief("fcntl", strerror(errno)); - } -} - -/* - * Setting TCP_NODELAY is not mandatory for the SPDY protocol. - */ -static void set_tcp_nodelay(int fd) -{ - int val = 1; - int rv; - rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); - if(rv == -1) { - dief("setsockopt", strerror(errno)); - } -} - -/* - * Update |pollfd| based on the state of |connection|. - */ -static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) -{ - pollfd->events = 0; - if(spdylay_session_want_read(connection->session) || - connection->want_io == WANT_READ) { - pollfd->events |= POLLIN; - } - if(spdylay_session_want_write(connection->session) || - connection->want_io == WANT_WRITE) { - pollfd->events |= POLLOUT; - } -} - -/* - * Submits the request |req| to the connection |connection|. This - * function does not send packets; just append the request to the - * internal queue in |connection->session|. - */ -static void submit_request(struct Connection *connection, struct Request *req) -{ - int pri = 0; - int rv; - const char *nv[15]; - /* We always use SPDY/3 style header even if the negotiated protocol - version is SPDY/2. The library translates the header name as - necessary. Make sure that the last item is NULL! */ - nv[0] = ":method"; nv[1] = "GET"; - nv[2] = ":path"; nv[3] = req->path; - nv[4] = ":version"; nv[5] = "HTTP/1.1"; - nv[6] = ":scheme"; nv[7] = "https"; - nv[8] = ":host"; nv[9] = req->hostport; - nv[10] = "accept"; nv[11] = "*/*"; - nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION; - nv[14] = NULL; - rv = spdylay_submit_request(connection->session, pri, nv, NULL, req); - if(rv != 0) { - diec("spdylay_submit_request", rv); - } -} - -/* - * Performs the network I/O. - */ -static void exec_io(struct Connection *connection) -{ - int rv; - rv = spdylay_session_recv(connection->session); - if(rv != 0) { - diec("spdylay_session_recv", rv); - } - rv = spdylay_session_send(connection->session); - if(rv != 0) { - diec("spdylay_session_send", rv); - } -} - -static void request_init(struct Request *req, const struct URI *uri) -{ - req->host = strcopy(uri->host, uri->hostlen); - req->port = uri->port; - req->path = strcopy(uri->path, uri->pathlen); - req->hostport = strcopy(uri->hostport, uri->hostportlen); - req->stream_id = -1; - req->inflater = NULL; -} - -static void request_free(struct Request *req) -{ - free(req->host); - free(req->path); - free(req->hostport); - spdylay_gzip_inflate_del(req->inflater); -} - -/* - * Fetches the resource denoted by |uri|. - */ -static void fetch_uri(const struct URI *uri) -{ - spdylay_session_callbacks callbacks; - int fd; - SSL_CTX *ssl_ctx; - SSL *ssl; - struct Request req; - struct Connection connection; - int rv; - nfds_t npollfds = 1; - struct pollfd pollfds[1]; - uint16_t spdy_proto_version; - - request_init(&req, uri); - - setup_spdylay_callbacks(&callbacks); - - /* Establish connection and setup SSL */ - fd = connect_to(req.host, req.port); - if (-1 == fd) - abort (); - ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - if(ssl_ctx == NULL) { - dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL)); - } - init_ssl_ctx(ssl_ctx, &spdy_proto_version); - ssl = SSL_new(ssl_ctx); - if(ssl == NULL) { - dief("SSL_new", ERR_error_string(ERR_get_error(), NULL)); - } - /* To simplify the program, we perform SSL/TLS handshake in blocking - I/O. */ - ssl_handshake(ssl, fd); - - connection.ssl = ssl; - connection.want_io = IO_NONE; - - /* Here make file descriptor non-block */ - make_non_block(fd); - set_tcp_nodelay(fd); - - printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version); - rv = spdylay_session_client_new(&connection.session, spdy_proto_version, - &callbacks, &connection); - if(rv != 0) { - diec("spdylay_session_client_new", rv); - } - - /* Submit the HTTP request to the outbound queue. */ - submit_request(&connection, &req); - - pollfds[0].fd = fd; - ctl_poll(pollfds, &connection); - - /* Event loop */ - while(spdylay_session_want_read(connection.session) || - spdylay_session_want_write(connection.session)) { - int nfds = poll(pollfds, npollfds, -1); - if(nfds == -1) { - dief("poll", strerror(errno)); - } - if(pollfds[0].revents & (POLLIN | POLLOUT)) { - exec_io(&connection); - } - if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { - die("Connection error"); - } - ctl_poll(pollfds, &connection); - } - - /* Resource cleanup */ - spdylay_session_del(connection.session); - SSL_shutdown(ssl); - SSL_free(ssl); - SSL_CTX_free(ssl_ctx); - shutdown(fd, SHUT_WR); - MHD_socket_close_(fd); - request_free(&req); -} - -static int parse_uri(struct URI *res, const char *uri) -{ - /* We only interested in https */ - size_t len, i, offset; - memset(res, 0, sizeof(struct URI)); - len = strlen(uri); - if(len < 9 || memcmp("https://", uri, 8) != 0) { - return -1; - } - offset = 8; - res->host = res->hostport = &uri[offset]; - res->hostlen = 0; - if(uri[offset] == '[') { - /* IPv6 literal address */ - ++offset; - ++res->host; - for(i = offset; i < len; ++i) { - if(uri[i] == ']') { - res->hostlen = i-offset; - offset = i+1; - break; - } - } - } else { - const char delims[] = ":/?#"; - for(i = offset; i < len; ++i) { - if(strchr(delims, uri[i]) != NULL) { - break; - } - } - res->hostlen = i-offset; - offset = i; - } - if(res->hostlen == 0) { - return -1; - } - /* Assuming https */ - res->port = 443; - if(offset < len) { - if(uri[offset] == ':') { - /* port */ - const char delims[] = "/?#"; - int port = 0; - ++offset; - for(i = offset; i < len; ++i) { - if(strchr(delims, uri[i]) != NULL) { - break; - } - if('0' <= uri[i] && uri[i] <= '9') { - port *= 10; - port += uri[i]-'0'; - if(port > 65535) { - return -1; - } - } else { - return -1; - } - } - if(port == 0) { - return -1; - } - offset = i; - res->port = port; - } - } - res->hostportlen = uri+offset-res->host; - for(i = offset; i < len; ++i) { - if(uri[i] == '#') { - break; - } - } - if(i-offset == 0) { - res->path = "/"; - res->pathlen = 1; - } else { - res->path = &uri[offset]; - res->pathlen = i-offset; - } - return 0; -} - - -/***** - * end of code needed to utilize spdylay - */ - - -/***** - * start of code needed to utilize microspdy - */ - - -void -standard_request_handler(void *cls, - struct SPDY_Request * request, - uint8_t priority, - const char *method, - const char *path, - const char *version, - const char *host, - const char *scheme, - struct SPDY_NameValue * headers, - bool more) -{ - (void)cls; - (void)request; - (void)priority; - (void)host; - (void)scheme; - (void)headers; - (void)method; - (void)version; - - struct SPDY_Response *response=NULL; - - if(strcmp(CLS,cls)!=0) - { - killchild(child,"wrong cls"); - } - - if(false != more){ - fprintf(stdout,"more has wrong value\n"); - exit(5); - } - - response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,RESPONSE_BODY,strlen(RESPONSE_BODY)); - - if(NULL==response){ - fprintf(stdout,"no response obj\n"); - exit(3); - } - - if(SPDY_queue_response(request,response,true,false,NULL,(void*)strdup(path))!=SPDY_YES) - { - fprintf(stdout,"queue\n"); - exit(4); - } -} - -void -session_closed_handler (void *cls, - struct SPDY_Session * session, - int by_client) -{ - printf("session_closed_handler called\n"); - - if(strcmp(CLS,cls)!=0) - { - killchild(child,"wrong cls"); - } - - if(SPDY_YES != by_client) - { - //killchild(child,"wrong by_client"); - printf("session closed by server\n"); - } - else - { - printf("session closed by client\n"); - } - - if(NULL == session) - { - killchild(child,"session is NULL"); - } - - session_closed_called = 1; -} - - -/***** - * end of code needed to utilize microspdy - */ - -//child process -void -childproc(int port) -{ - struct URI uri; - struct sigaction act; - int rv; - char *uristr; - - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, 0); - - asprintf(&uristr, "https://127.0.0.1:%i/",port); - if(NULL == (rcvbuf = malloc(strlen(RESPONSE_BODY)+1))) - killparent(parent,"no memory"); - - SSL_load_error_strings(); - SSL_library_init(); - - rv = parse_uri(&uri, uristr); - if(rv != 0) { - killparent(parent,"parse_uri failed"); - } - fetch_uri(&uri); - - if(strcmp(rcvbuf, RESPONSE_BODY)) - killparent(parent,"received data is different"); -} - -//parent proc -int -parentproc( int port) -{ - int childstatus; - unsigned long long timeoutlong=0; - struct timeval timeout; - int ret; - fd_set read_fd_set; - fd_set write_fd_set; - fd_set except_fd_set; - int maxfd = -1; - struct SPDY_Daemon *daemon; - - SPDY_init(); - - daemon = SPDY_start_daemon(port, - DATA_DIR "cert-and-key.pem", - DATA_DIR "cert-and-key.pem", - NULL,&session_closed_handler,&standard_request_handler,NULL,CLS,SPDY_DAEMON_OPTION_END); - - if(NULL==daemon){ - printf("no daemon\n"); - return 1; - } - - do - { - FD_ZERO(&read_fd_set); - FD_ZERO(&write_fd_set); - FD_ZERO(&except_fd_set); - - ret = SPDY_get_timeout(daemon, &timeoutlong); - if(SPDY_NO == ret || timeoutlong > 1000) - { - timeout.tv_sec = 1; - timeout.tv_usec = 0; - } - else - { - timeout.tv_sec = timeoutlong / 1000; - timeout.tv_usec = (timeoutlong % 1000) * 1000; - } - - maxfd = SPDY_get_fdset (daemon, - &read_fd_set, - &write_fd_set, - &except_fd_set); - - ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); - - switch(ret) { - case -1: - printf("select error: %i\n", errno); - killchild(child, "select error"); - break; - case 0: - - break; - default: - SPDY_run(daemon); - - break; - } - } - while(waitpid(child,&childstatus,WNOHANG) != child); - - //give chance to the client to close socket and handle this in run - usleep(100000); - SPDY_run(daemon); - - SPDY_stop_daemon(daemon); - - SPDY_deinit(); - - return WEXITSTATUS(childstatus); -} - -int main() -{ - int port = get_port(12123); - parent = getpid(); - - child = fork(); - if (child == -1) - { - fprintf(stderr, "can't fork, error %d\n", errno); - exit(EXIT_FAILURE); - } - - if (child == 0) - { - childproc(port); - _exit(0); - } - else - { - int ret = parentproc(port); - if(1 == session_closed_called && 0 == ret) - exit(0); - else - exit(ret ? ret : 21); - } - return 1; -} diff --git a/src/testspdy/test_request_response_with_callback.c b/src/testspdy/test_request_response_with_callback.c @@ -1,320 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file request_response_with_callback.c - * @brief tests responses with callbacks - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "microspdy.h" -#include "stdio.h" -#include <sys/wait.h> -#include <ctype.h> -#include "common.h" -#include <sys/time.h> -#include <sys/stat.h> - -int port; - -pid_t parent; -pid_t child; - -int run = 1; -int chunk_size=1; - -void -killchild() -{ - kill(child, SIGKILL); - exit(1); -} - -void -killparent() -{ - kill(parent, SIGKILL); - _exit(1); -} - -ssize_t -response_callback (void *cls, - void *buffer, - size_t max, - bool *more) -{ - FILE *fd =(FILE*)cls; - - size_t n; - if(chunk_size % 2) - n = chunk_size; - else - n = max - chunk_size; - - if(n < 1) n = 1; - else if (n > max) n=max; - chunk_size++; - - int ret = fread(buffer,1,n,fd); - *more = feof(fd) == 0; - - //printf("more is %i\n",*more); - - if(!(*more)) - fclose(fd); - - return ret; -} - - -void -response_done_callback(void *cls, - struct SPDY_Response * response, - struct SPDY_Request * request, - enum SPDY_RESPONSE_RESULT status, - bool streamopened) -{ - (void)status; - (void)streamopened; - - printf("answer for %s was sent\n", (char*)cls); - - SPDY_destroy_request(request); - SPDY_destroy_response(response); - free(cls); - - run = 0; -} - -void -standard_request_handler(void *cls, - struct SPDY_Request * request, - uint8_t priority, - const char *method, - const char *path, - const char *version, - const char *host, - const char *scheme, - struct SPDY_NameValue * headers, - bool more) -{ - (void)cls; - (void)request; - (void)priority; - (void)host; - (void)scheme; - (void)headers; - (void)method; - (void)version; - (void)more; - - struct SPDY_Response *response=NULL; - struct SPDY_NameValue *resp_headers; - - printf("received request for '%s %s %s'\n", method, path, version); - - FILE *fd = fopen(DATA_DIR "spdy-draft.txt","r"); - - if(NULL == (resp_headers = SPDY_name_value_create())) - { - fprintf(stdout,"SPDY_name_value_create failed\n"); - killchild(); - } - if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_TYPE,"text/plain")) - { - fprintf(stdout,"SPDY_name_value_add failed\n"); - killchild(); - } - - response = SPDY_build_response_with_callback(200,NULL, - SPDY_HTTP_VERSION_1_1,resp_headers,&response_callback,fd,SPDY_MAX_SUPPORTED_FRAME_SIZE); - SPDY_name_value_destroy(resp_headers); - - if(NULL==response){ - fprintf(stdout,"no response obj\n"); - killchild(); - } - - void *clspath = strdup(path); - - if(SPDY_queue_response(request,response,true,false,&response_done_callback,clspath)!=SPDY_YES) - { - fprintf(stdout,"queue\n"); - killchild(); - } -} - -int -parentproc() -{ - int childstatus; - unsigned long long timeoutlong=0; - struct timeval timeout; - int ret; - fd_set read_fd_set; - fd_set write_fd_set; - fd_set except_fd_set; - int maxfd = -1; - struct SPDY_Daemon *daemon; - - SPDY_init(); - - daemon = SPDY_start_daemon(port, - DATA_DIR "cert-and-key.pem", - DATA_DIR "cert-and-key.pem", - NULL, - NULL, - &standard_request_handler, - NULL, - NULL, - SPDY_DAEMON_OPTION_SESSION_TIMEOUT, - 1800, - SPDY_DAEMON_OPTION_END); - - if(NULL==daemon){ - printf("no daemon\n"); - return 1; - } - - do - { - FD_ZERO(&read_fd_set); - FD_ZERO(&write_fd_set); - FD_ZERO(&except_fd_set); - - ret = SPDY_get_timeout(daemon, &timeoutlong); - if(SPDY_NO == ret || timeoutlong > 1000) - { - timeout.tv_sec = 1; - timeout.tv_usec = 0; - } - else - { - timeout.tv_sec = timeoutlong / 1000; - timeout.tv_usec = (timeoutlong % 1000) * 1000; - } - - maxfd = SPDY_get_fdset (daemon, - &read_fd_set, - &write_fd_set, - &except_fd_set); - - ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); - - switch(ret) { - case -1: - printf("select error: %i\n", errno); - break; - case 0: - - break; - default: - SPDY_run(daemon); - - break; - } - } - while(waitpid(child,&childstatus,WNOHANG) != child); - - SPDY_stop_daemon(daemon); - - SPDY_deinit(); - - return WEXITSTATUS(childstatus); -} - -#define MD5_LEN 32 - -int -md5(char *cmd, char *md5_sum) -{ - FILE *p = popen(cmd, "r"); - if (p == NULL) return 0; - - int i, ch; - for (i = 0; i < MD5_LEN && isxdigit(ch = fgetc(p)); i++) { - *md5_sum++ = ch; - } - - *md5_sum = '\0'; - pclose(p); - return i == MD5_LEN; -} - -int -childproc() -{ - char *cmd1; - char *cmd2; - char md5_sum1[33]; - char md5_sum2[33]; - int ret; - struct timeval tv1; - struct timeval tv2; - struct stat st; - //int secs; - uint64_t usecs; - - asprintf(&cmd1, "spdycat https://127.0.0.1:%i/ | md5sum",port); - asprintf(&cmd2, "md5sum " DATA_DIR "spdy-draft.txt"); - - gettimeofday(&tv1, NULL); - md5(cmd1,md5_sum1); - gettimeofday(&tv2, NULL); - md5(cmd2,md5_sum2); - - printf("downloaded file md5: %s\n", md5_sum1); - printf("original file md5: %s\n", md5_sum2); - ret = strcmp(md5_sum1, md5_sum2); - - if(0 == ret && 0 == stat(DATA_DIR "spdy-draft.txt", &st)) - { - usecs = (uint64_t)1000000 * (uint64_t)(tv2.tv_sec - tv1.tv_sec) + tv2.tv_usec - tv1.tv_usec; - printf("%lld bytes read in %llu usecs\n", (long long)st.st_size, (long long unsigned )usecs); - } - - return ret; -} - - -int -main() -{ - port = get_port(11123); - parent = getpid(); - - child = fork(); - if (-1 == child) - { - fprintf(stderr, "can't fork, error %d\n", errno); - exit(EXIT_FAILURE); - } - - if (child == 0) - { - int ret = childproc(); - _exit(ret); - } - else - { - int ret = parentproc(); - exit(ret); - } - return 1; -} diff --git a/src/testspdy/test_requests_with_assets.c b/src/testspdy/test_requests_with_assets.c @@ -1,302 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file requests_with_assets.c - * @brief tests several requests for an HTML and all its assets. For - * client spdycat (from spdylay) is used. The latter uses - * libxml2. - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "microspdy.h" -#include "common.h" -#include <sys/wait.h> -#include <stdio.h> /* printf, stderr, fprintf */ -#include <sys/types.h> /* pid_t */ -#include <unistd.h> /* _exit, fork */ -#include <stdlib.h> /* exit */ -#include <errno.h> /* errno */ -#include <sys/wait.h> /* pid_t */ -#include "common.h" - -#define HTML "<html>\ -<head>\ -<link href=\"file1.css\" rel=\"stylesheet\" type=\"text/css\" />\ -<link href=\"file2.css\" rel=\"stylesheet\" type=\"text/css\" />\ -<link href=\"file3.css\" rel=\"stylesheet\" type=\"text/css\" />\ -</head>\ -<body><b>Hi, this is libmicrospdy!</b>\ -</body></html>" - -#define CSS "@media all{body{font-family:verdana,arial;color:#333;background-color:#fff;margin:0;padding:0}#navcontainer ul{padding-left:0;background:#005cb9 url(http://cdn.computerhope.com/backbar.jpg) repeat-x left top;padding-bottom:0;padding-top:0;color:#fff;float:right;font-weight:700;width:100%;border-top:1px solid #333;border-bottom:1px solid #333;margin:0}#navcontainer ul li a{color:#fff;text-decoration:none;float:left;border-top:1px solid #fff;border-right:1px solid #333;border-left:1px solid #fff;border-bottom:1px solid #333;padding:.2em 1em}#navcontainer ul li a:hover{background:url(http://cdn.computerhope.com/backbar2.jpg) repeat-x left top;background-color:#9fcfff;color:#333;border-top:1px solid #333;border-right:1px solid #fff;border-left:1px solid #333;border-bottom:1px solid #fff}a:visited{color:#636}a{color:#2a70d0}#content a{text-decoration:none;border-bottom:1px solid #DBDBDB}#content a:hover,a:active,a:focus{color:#c33;border-bottom:1px solid #c33}img{border:0}#content-container1{float:left;width:100%;background:#fff url(http://cdn.computerhope.com/back.jpg) repeat-y 0}.print,.email,.edit,.share,.up,.down,.book,.folder,.issue,.driver,.history,.news,.btips,.tips,.warn,.phone,.forum,.question{background:url(chs.png) no-repeat top left}#container{padding-left:150px;padding-right:265px}#container .column{position:relative;float:left}#content{width:100%;padding:20px}#left-bar{width:150px;margin-left:-100%;left:225px;padding:10px}#container > #left-bar{left:-190px}#right-bar{width:205px;margin-right:-265px;padding:0 10px}#topad{background:#9fcfff;text-align:center;padding:35px 0 4px}#leftad{clear:both;background:inherit;height:auto;margin:15px 0 0}#content ul{position:relative;margin:10px 0 10px 10px;padding:0}#content ul li{list-style-type:none;background:url(http://cdn.computerhope.com/arrow.png) no-repeat top left;background-position:0 5px;line-height:1.5625;padding:0 0 8px 23px}ol li{margin-bottom:8px;line-height:1.5625}.print,.email,.edit,.share{padding-left:23px}.print{background-position:0 -868px;width:16px;height:16px}.email{background-position:0 -469px;width:16px;height:16px}.edit{background-position:0 -403px;width:16px;height:16px}.share{background-position:0 -1002px;width:16px;height:16px}#left-bar li.title{color:#005cb9;font-weight:700;margin:1em 0}#right-box{width:180px;border:1px solid #005cb9;border-radius:15px 15px 15px 15px;background:#ebebeb;margin:90px 0 0;padding:10px}#right-box ul.poll{margin-top:15px;font-weight:700;margin-bottom:10px}.up,.down{padding-left:20px;text-decoration:none;color:#333}.up{background-position:0 -1068px;width:16px;height:16px}.down{background-position:0 -269px;width:16px;height:16px}#right-box li.title{color:#333;font-weight:700;margin:1em 0 0}#header{background:#9fcfff}#containercol2{background-color:#d0e8ff;width:700px;overflow:hidden;margin:0 auto}#containercol2 ul.col2{width:700px;list-style:none;float:left;padding:0}#containercol2 ul.col2 li h2{border:1px solid #005cb9;background:url(http://cdn.computerhope.com/backbar.jpg) repeat-x left top;color:#fff;font-size:large;text-align:center}#containercol2 ul.col2 li{float:left;width:340px;padding:5px}#containercol2 ul li.headline{border-bottom:1px solid #327dac;background:gray}#bottomad{margin:14px 0 0}input.btn,input.bbtn{color:#333;background:#9fcfff;font-weight:700;border:1px solid #005cb9;border-top:1px solid #eee;border-left:1px solid #eee;cursor:pointer;margin:4px 0 0}input.sbar,input.bsbar{color:#333;width:110px;background:#fff}input.btn{width:115px;font-size:medium}input.sbar{font-size:medium}input.bbtn{width:110px;font-size:large}input.bsbar{width:350px;font-size:18px;margin-right:5px}h1{font-size:175%;margin-bottom:25px;border-bottom:1px solid #dadada;padding-bottom:.17em;letter-spacing:-.05em;font-weight:700}.ce{text-align:center}.tab{margin-left:40px}p{line-height:1.5625}.tabb{margin-left:40px;font-weight:700;line-height:1.4}.dtab{margin-left:80px}.dd{font-weight:700;margin-left:7px}.lb{margin-left:5px}.bld{font-weight:700}.bb{font-size:14pt;color:#005cb9;font-weight:700}.bbl{font-size:14pt;font-weight:700}.nb{color:#005cb9;font-weight:700}.rg{color:gray;font-weight:700}.sg{font-size:10pt;color:gray}.sm{font-size:small}.rb{color:#fff;font-weight:700;text-indent:.3cm}.wt{color:#fff;font-weight:700}.bwt{color:#fff;font-weight:700;font-size:14pt}.large{font-size:x-large}.red{color:red}table{clear:both}.mtable,.mtable2{border:0 solid silver;background-color:#e5e5e5;border-spacing:2px 1px;width:98%;margin-left:auto;margin-right:auto}table.mtable td,table.mtable2 td{border-spacing:5px 10px;padding:9px}table.mtable th,table.mtable2 th{background:#005cb9 url(http://cdn.computerhope.com/backbar.jpg) repeat-x left top;color:#fff;font-weight:700;padding:5px}table.mtable a{border-bottom:0!important}table.mtable tr:hover td{background-color:#eee;cursor:pointer}td{vertical-align:top}.tcb{background:#005cb9 url(http://cdn.computerhope.com/backbar.jpg) repeat-x left top}.tclb{background-color:#9fcfff}.tcllb{background-color:#d0e8ff}.tcw{background-color:#fff}.tcg{background-color:#ebebeb}.tcbl{background-color:#333}.tcy{border:1px solid #005cb9;background-color:#f1f5f9;overflow:auto;padding:15px}.icell{padding-left:15px;padding-bottom:3px}.mlb{background-color:#9fcfff;padding-left:15px;padding-bottom:3px;white-space:nowrap;width:120px;vertical-align:top}#footer{background:url(http://cdn.computerhope.com/footback.jpg) repeat-x left top;background-color:#d0e8ff;clear:both;padding:5px}#footer ul li{list-style-type:none;display:inline;background:inherit;margin:0}#footer li a{float:left;text-decoration:none;width:300px;border-bottom:1px dotted #327dac;padding:0 0 10px 10px}#footer li a:hover{background:#005cb9;color:#fff}#creditfooter{display:none}.legal{text-align:center;font-size:11px}.legal a{text-decoration:none;color:#333}.floatLeft{float:left;clear:left;margin-right:20px;margin-bottom:10px}.floatRight{float:right;margin-left:20px;margin-bottom:10px}.floatRightClear{float:right;clear:right;margin-left:20px}:first-child + html #container{overflow:hidden}.book,.folder,.issue,.driver,.history,.news,.btips,.tips,.warn,.phone,.forum,.question{padding-left:22px;font-weight:700}.book{background-position:0 0;width:17px;height:18px}.tips{background-position:0 -68px;width:17px;height:17px}.btips{background-position:0 -135px;width:17px;height:17px}.history{background-position:0 -202px;width:17px;height:17px}.driver{background-position:0 -335px;width:17px;height:18px}.folder{background-position:0 -535px;width:17px;height:16px}.issue{background-position:0 -601px;width:17px;height:18px}.news{background-position:0 -669px;width:17px;height:14px}.forum{background-position:0 -733px;width:17px;height:18px}.phone{background-position:0 -801px;width:17px;height:17px}.question{background-position:0 -934px;width:17px;height:18px}.warn{background-position:0 -1134px;width:16px;height:16px}textarea,input{border:1px solid #ccc;border-top:1px solid #8d8e90;border-left:1px solid #8d8e90}textarea:focus,input:focus{border:1px solid #005cb9}#left-bar ul,#right-box ul,#footer ul{margin:0;padding:0}#right-box li.poll,#navcontainer ul li{display:inline}#noprint{margin:1px 0 0}#left-bar ul li,#right-box ul li{margin-left:10px;list-style-type:none;padding:0}#right-box a,#left-bar a{color:#333}}@media print{#header,#navcontainer,#topad,#left-bar,#right-bar,#bottomad,#footer,#search,#buttons,#noprint{display:none!important}#content a{text-decoration:none;color:#000}#content,#container{font-family:\"Times New Roman\",Times;background:transparent!important;text-indent:0!important;width:100%!important;border:0!important;float:none!important;position:static!important;overflow:visible!important;line-height:1;margin:0!important;padding:0!important}h1{font-size:14pt;margin-bottom:5px;border-bottom:0;padding-bottom:0;letter-spacing:-.05em;font-weight:700}h2{font-size:13pt}.bb{font-size:13pt;color:#005cb9;font-weight:700}#content ul li:before{content:\"\00bb \0020\"}#content .nb,.bb{font-weight:700;color:#000}table{margin-top:30px;margin-bottom:30px;border-collapse:collapse}th,td{border:1px solid #333}}" - -#define JS "var _gaq = _gaq || [];\ -_gaq.push(['_setAccount', 'UA-222222222222222222222222-1']);\ -_gaq.push(['_trackPageview']);\ -(function() {\ -var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;\ -//ga.src = ('https:' == document.location.protocol ? 'ZZZhttps://ssl' : 'ZZZhttp://www') + '.google-analytics.com/ga.js';\ -var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);\ -})();" - -int port; - -#define NUM_CLIENTS 50 - -pid_t parent; -int html_req_count; -int html_resp_count; - -int child_c; -int children_pid[NUM_CLIENTS]; -int children_status[NUM_CLIENTS]; - -int session_closed_called; - -void -new_child(pid_t pid) -{ - //todo ids overflow - children_pid[child_c] = pid; - children_status[child_c++] = 1; -} - -int -alive_children() -{ - int i; - int dead; - int status; - int ret = 0; - - for(i=NUM_CLIENTS-1;i>=0;--i) - { - if (1 != children_status[i]) - continue; - dead = waitpid(children_pid[i], &status, WNOHANG); - if (0 == dead) - { - ret = 1; - continue; - } - if (WEXITSTATUS(status) != 0) - { - for (i=NUM_CLIENTS-1;i>=0;--i) - if (1 == children_status[i]) - kill (children_pid[i], SIGKILL); - exit (WEXITSTATUS(status)); - } - children_status[i] = 2; - } - return ret; -} - -void -killchild(int pid, char *message) -{ - printf("%s\nkilling %i\n",message,pid); - kill(pid, SIGKILL); - exit(1); -} - -void -killparent(int pid, const char *message) -{ - printf("%s\nkilling %i\n",message,pid); - kill(pid, SIGKILL); - _exit(1); -} - -void -standard_request_handler(void *cls, - struct SPDY_Request * request, - uint8_t priority, - const char *method, - const char *path, - const char *version, - const char *host, - const char *scheme, - struct SPDY_NameValue * headers, - bool more) -{ - (void)cls; - (void)request; - (void)priority; - (void)host; - (void)scheme; - (void)headers; - (void)method; - (void)version; - (void)more; - - struct SPDY_Response *response; - - if(NULL != strstr(path,".css")) - { - response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,CSS,strlen(CSS)); - if(strcmp("/file3.css",path)==0)++html_resp_count; - } - /*else if(NULL != strstr(path,".js")) - { - response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,JS,strlen(JS)); - }*/ - else - { - response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,HTML,strlen(HTML)); - } - - if(NULL==response){ - fprintf(stdout,"no response obj\n"); - exit(3); - } - - if(SPDY_queue_response(request,response,true,false,NULL,(void*)strdup(path))!=SPDY_YES) - { - fprintf(stdout,"queue\n"); - exit(4); - } - - -} - -void -run_spdycat() -{ - pid_t child; - ++html_req_count; - child = fork(); - if (child == -1) - { - killparent(parent,"fork failed\n"); - } - - if (child == 0) - { - int devnull; - - close(1); - devnull = open("/dev/null", O_WRONLY); - if (1 != devnull) - { - dup2(devnull, 1); - close(devnull); - } - char *uri; - asprintf (&uri, "https://127.0.0.1:%i/%i.html", port, html_req_count); - execlp ("spdycat", "spdycat", "-anv", uri, NULL); - killparent (parent, "executing spdycat failed"); - } - else - { - new_child(child); - } -} - -int -parentproc() -{ - unsigned long long timeoutlong=0; - struct timeval timeout; - int ret; - fd_set read_fd_set; - fd_set write_fd_set; - fd_set except_fd_set; - int maxfd = -1; - struct SPDY_Daemon *daemon; - - SPDY_init(); - - daemon = SPDY_start_daemon(port, - DATA_DIR "cert-and-key.pem", - DATA_DIR "cert-and-key.pem", - NULL, - NULL, - &standard_request_handler, - NULL, - NULL, - SPDY_DAEMON_OPTION_END); - - if(NULL==daemon){ - printf("no daemon\n"); - return 1; - } - - do - { - if(NUM_CLIENTS > html_req_count) - { - run_spdycat(); - } - - FD_ZERO(&read_fd_set); - FD_ZERO(&write_fd_set); - FD_ZERO(&except_fd_set); - - ret = SPDY_get_timeout(daemon, &timeoutlong); - if(SPDY_NO == ret || timeoutlong > 1000) - { - timeout.tv_sec = 1; - timeout.tv_usec = 0; - } - else - { - timeout.tv_sec = timeoutlong / 1000; - timeout.tv_usec = (timeoutlong % 1000) * 1000; - } - - maxfd = SPDY_get_fdset (daemon, - &read_fd_set, - &write_fd_set, - &except_fd_set); - - ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); - - switch(ret) { - case -1: - printf("select error: %i\n", errno); - break; - case 0: - - break; - default: - SPDY_run(daemon); - - break; - } - } - while(alive_children()); - - SPDY_stop_daemon(daemon); - - SPDY_deinit(); - - return html_resp_count != html_req_count; -} - -int main() -{ - parent = getpid(); - port = get_port(10123); - - int ret = parentproc(); - exit(ret); - - return 1; -} diff --git a/src/testspdy/test_session_timeout.c b/src/testspdy/test_session_timeout.c @@ -1,338 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2013 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file session_timeout.c - * @brief tests closing sessions after set timeout. Openssl is used for - * client - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "microspdy.h" -#include "stdio.h" -#include <sys/wait.h> -#include <ctype.h> -#include "common.h" -#include <sys/time.h> -#include <sys/stat.h> -#include "../microspdy/internal.h" - -#define TIMEOUT 2 -#define SELECT_MS_TIMEOUT 20 - -int port; - -pid_t parent; -pid_t child; - -int run = 1; -int chunk_size=1; -int new_session; -int closed_session; -int do_sleep; - - - -static unsigned long long -monotonic_time (void) -{ -#ifdef HAVE_CLOCK_GETTIME -#ifdef CLOCK_MONOTONIC - struct timespec ts; - if (0 == clock_gettime (CLOCK_MONOTONIC, &ts)) - return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; -#endif -#endif - return time (NULL) * 1000; -} - - -static void -killchild(char *msg) -{ - printf("%s\n",msg); - kill(child, SIGKILL); - exit(1); -} - - -static void -killparent(char *msg) -{ - printf("%s\n",msg); - kill(parent, SIGKILL); - _exit(1); -} - - -static void -new_session_cb (void *cls, - struct SPDY_Session * session) -{ - (void)cls; - (void)session; - - if(!new_session)do_sleep = 1; - new_session = 1; - printf("new session\n"); -} - - -static void -closed_session_cb (void *cls, - struct SPDY_Session * session, - int by_client) -{ - (void)cls; - (void)session; - - printf("closed_session_cb called\n"); - - if(SPDY_YES == by_client) - { - killchild("closed by the client"); - } - if(closed_session) - { - killchild("closed_session_cb called twice"); - } - - closed_session = 1; -} - - -static int -parentproc() -{ - int childstatus; - unsigned long long timeoutlong=0; - struct timeval timeout; - int ret; - fd_set read_fd_set; - fd_set write_fd_set; - fd_set except_fd_set; - int maxfd = -1; - struct SPDY_Daemon *daemon; - unsigned long long beginning = 0; - unsigned long long now; - - SPDY_init(); - - daemon = SPDY_start_daemon(port, - DATA_DIR "cert-and-key.pem", - DATA_DIR "cert-and-key.pem", - &new_session_cb, - &closed_session_cb, - NULL, - NULL, - NULL, - SPDY_DAEMON_OPTION_SESSION_TIMEOUT, - TIMEOUT, - SPDY_DAEMON_OPTION_END); - - if(NULL==daemon){ - printf("no daemon\n"); - return 1; - } - - do - { - do_sleep=0; - FD_ZERO(&read_fd_set); - FD_ZERO(&write_fd_set); - FD_ZERO(&except_fd_set); - - ret = SPDY_get_timeout(daemon, &timeoutlong); - - if(new_session && !closed_session) - { - if(SPDY_NO == ret) - { - killchild("SPDY_get_timeout returned wrong SPDY_NO"); - } - /*if(timeoutlong) - { - killchild("SPDY_get_timeout returned wrong timeout"); - }*/ - now = monotonic_time (); - if(now - beginning > TIMEOUT*1000 + SELECT_MS_TIMEOUT) - { - printf("Started at: %llums\n",beginning); - printf("Now is: %llums\n",now); - printf("Timeout is: %i\n",TIMEOUT); - printf("Select Timeout is: %ims\n",SELECT_MS_TIMEOUT); - printf("SPDY_get_timeout gave: %llums\n",timeoutlong); - killchild("Timeout passed but session was not closed"); - } - if(timeoutlong > beginning + TIMEOUT *1000) - { - printf("Started at: %llums\n",beginning); - printf("Now is: %llums\n",now); - printf("Timeout is: %i\n",TIMEOUT); - printf("Select Timeout is: %ims\n",SELECT_MS_TIMEOUT); - printf("SPDY_get_timeout gave: %llums\n",timeoutlong); - killchild("SPDY_get_timeout returned wrong timeout"); - } - } - else - { - if(SPDY_YES == ret) - { - killchild("SPDY_get_timeout returned wrong SPDY_YES"); - } - } - - if(SPDY_NO == ret || timeoutlong >= 1000) - { - timeout.tv_sec = 1; - timeout.tv_usec = 0; - } - else - { - timeout.tv_sec = timeoutlong / 1000; - timeout.tv_usec = (timeoutlong % 1000) * 1000; - } - - //ignore values - timeout.tv_sec = 0; - timeout.tv_usec = SELECT_MS_TIMEOUT * 1000; - - maxfd = SPDY_get_fdset (daemon, - &read_fd_set, - &write_fd_set, - &except_fd_set); - - ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); - - switch(ret) { - case -1: - printf("select error: %i\n", errno); - break; - case 0: - /*if(new_session) - { - killchild("select returned wrong number"); - }*/ - break; - default: - SPDY_run(daemon); - if(0 == beginning) - { - beginning = monotonic_time (); - } - /*if(do_sleep) - { - sleep(TIMEOUT); - do_sleep = 0; - }*/ - break; - } - } - while(waitpid(child,&childstatus,WNOHANG) != child); - - if(!new_session || !closed_session) - { - killchild("child is dead, callback wasn't called"); - } - - ret = SPDY_get_timeout(daemon, &timeoutlong); - - if(SPDY_YES == ret) - { - killchild("SPDY_get_timeout returned wrong SPDY_YES after child died"); - } - - SPDY_stop_daemon(daemon); - - SPDY_deinit(); - - return 0; -} - - -static int -childproc() -{ - pid_t devnull; - int out; - - out=dup(1); - if (-1 == out) - abort(); - //close(0); - close(1); - close(2); - /*devnull = open("/dev/null", O_RDONLY); - if (0 != devnull) - { - dup2(devnull, 0); - close(devnull); - }*/ - devnull = open("/dev/null", O_WRONLY); - if (-1 == devnull) - abort (); - if (1 != devnull) - { - dup2(devnull, 1); - close(devnull); - } - devnull = open("/dev/null", O_WRONLY); - if (-1 == devnull) - abort (); - if (2 != devnull) - { - dup2(devnull, 2); - close(devnull); - } - char *uri; - asprintf (&uri, "127.0.0.1:%i", port); - execlp ("openssl", "openssl", "s_client", "-connect", uri, NULL); - close(1); - dup2(out,1); - close(out); - killparent ("executing openssl failed"); - return 1; -} - - -int -main() -{ - port = get_port(11123); - parent = getpid(); - - child = fork(); - if (-1 == child) - { - fprintf(stderr, "can't fork, error %d\n", errno); - exit(EXIT_FAILURE); - } - - if (child == 0) - { - int ret = childproc(); - _exit(ret); - } - else - { - int ret = parentproc(); - exit(ret); - } - return 1; -} diff --git a/src/testspdy/test_struct_namevalue.c b/src/testspdy/test_struct_namevalue.c @@ -1,346 +0,0 @@ -/* - This file is part of libmicrospdy - Copyright Copyright (C) 2012 Andrey Uzunov - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file struct_namevalue.c - * @brief tests all the API functions for handling struct SPDY_NameValue - * @author Andrey Uzunov - */ - -#include "platform.h" -#include "microspdy.h" -#include "common.h" -#include "../microspdy/structures.h" -#include "../microspdy/alstructures.h" - -char *pairs[] = {"one","1","two","2","three","3","four","4","five","5"}; -char *pairs_with_dups[] = {"one","1","two","2","one","11","two","22","three","3","two","222","two","2222","four","","five","5"};//82 -char *pairs_with_empty[] = {"name","","name","value"}; -char *pairs_different[] = {"30","thirty","40","fouthy"}; -int size; -int size2; -int brake_at = 3; -bool flag; - - -int -iterate_cb (void *cls, const char *name, const char * const * value, int num_values) -{ - int *c = (int*)cls; - - if(*c < 0 || *c > size) - exit(11); - - if(strcmp(name,pairs[*c]) != 0) - { - FAIL_TEST("name is wrong\n"); - } - - if(1 != num_values) - { - FAIL_TEST("num_values is wrong\n"); - } - - if(strcmp(value[0],pairs[(*c)+1]) != 0) - { - FAIL_TEST("value is wrong\n"); - } - - (*c)+=2; - - return SPDY_YES; -} - -int -iterate_brake_cb (void *cls, const char *name, const char * const *value, int num_values) -{ - (void)name; - (void)value; - (void)num_values; - - int *c = (int*)cls; - - if(*c < 0 || *c >= brake_at) - { - FAIL_TEST("iteration was not interrupted\n"); - } - - (*c)++; - - if(*c == brake_at) return SPDY_NO; - - return SPDY_YES; -} - -int -main() -{ - SPDY_init(); - - const char *const*value; - const char *const*value2; - int i; - int j; - int cls = 0; - int ret; - int ret2; - void *ob1; - void *ob2; - void *ob3; - void *stream; - char data[] = "anything"; - struct SPDY_NameValue *container; - struct SPDY_NameValue *container2; - struct SPDY_NameValue *container3; - struct SPDY_NameValue *container_arr[2]; - - size = sizeof(pairs)/sizeof(pairs[0]); - - if(NULL == (container = SPDY_name_value_create ())) - { - FAIL_TEST("SPDY_name_value_create failed\n"); - } - - if(NULL != SPDY_name_value_lookup (container, "anything", &ret)) - { - FAIL_TEST("SPDY_name_value_lookup failed\n"); - } - - if(SPDY_name_value_iterate (container, NULL, NULL) != 0) - { - FAIL_TEST("SPDY_name_value_iterate failed\n"); - } - - for(i=0;i<size; i+=2) - { - if(SPDY_YES != SPDY_name_value_add(container,pairs[i],pairs[i+1])) - { - FAIL_TEST("SPDY_name_value_add failed\n"); - } - - if(SPDY_name_value_iterate (container, NULL, NULL) != ((i / 2) + 1)) - { - FAIL_TEST("SPDY_name_value_iterate failed\n"); - } - } - - if(NULL != SPDY_name_value_lookup (container, "anything", &ret)) - { - FAIL_TEST("SPDY_name_value_lookup failed\n"); - } - - for(i=size - 2; i >= 0; i-=2) - { - value = SPDY_name_value_lookup(container,pairs[i], &ret); - if(NULL == value || 1 !=ret || strcmp(value[0], pairs[i+1]) != 0) - { - printf("%p; %i; %i\n", value, ret, - (NULL == value) ? -1 : strcmp(value[0], pairs[i+1])); - FAIL_TEST("SPDY_name_value_lookup failed\n"); - } - } - - SPDY_name_value_iterate (container, &iterate_cb, &cls); - - cls = 0; - if(SPDY_name_value_iterate (container, &iterate_brake_cb, &cls) != brake_at) - { - FAIL_TEST("SPDY_name_value_iterate with brake failed\n"); - } - - SPDY_name_value_destroy(container); - - //check everything with NULL values - for(i=0; i<7; ++i) - { - printf("%i ",i); - ob1 = (i & 4) ? data : NULL; - ob2 = (i & 2) ? data : NULL; - ob3 = (i & 1) ? data : NULL; - if(SPDY_INPUT_ERROR != SPDY_name_value_add(ob1,ob2,ob3)) - { - FAIL_TEST("SPDY_name_value_add with NULLs failed\n"); - } - } - printf("\n"); - fflush(stdout); - - if(SPDY_INPUT_ERROR != SPDY_name_value_iterate(NULL,NULL,NULL)) - { - FAIL_TEST("SPDY_name_value_iterate with NULLs failed\n"); - } - - for(i=0; i<7; ++i) - { - printf("%i ",i); - ob1 = (i & 4) ? data : NULL; - ob2 = (i & 2) ? data : NULL; - ob3 = (i & 1) ? data : NULL; - if(NULL != SPDY_name_value_lookup(ob1,ob2,ob3)) - { - FAIL_TEST("SPDY_name_value_lookup with NULLs failed\n"); - } - } - printf("\n"); - SPDY_name_value_destroy(NULL); - - if(NULL == (container = SPDY_name_value_create ())) - { - FAIL_TEST("SPDY_name_value_create failed\n"); - } - - size = sizeof(pairs_with_dups)/sizeof(pairs_with_dups[0]); - - for(i=0;i<size; i+=2) - { - if(SPDY_YES != SPDY_name_value_add(container,pairs_with_dups[i],pairs_with_dups[i+1])) - { - FAIL_TEST("SPDY_name_value_add failed\n"); - } - } - if(SPDY_name_value_iterate (container, NULL, NULL) != atoi(pairs_with_dups[size - 1])) - { - FAIL_TEST("SPDY_name_value_iterate failed\n"); - } - for(i=size - 2; i >= 0; i-=2) - { - value = SPDY_name_value_lookup(container,pairs_with_dups[i], &ret); - if(NULL == value) - { - FAIL_TEST("SPDY_name_value_lookup failed\n"); - } - flag = false; - for(j=0; j<ret; ++j) - if(0 == strcmp(pairs_with_dups[i + 1], value[j])) - { - if(flag) - FAIL_TEST("SPDY_name_value_lookup failed\n"); - flag=true; - } - - if(!flag) - FAIL_TEST("SPDY_name_value_lookup failed\n"); - } - if(SPDY_NO != SPDY_name_value_add(container,pairs_with_dups[0],pairs_with_dups[1])) - FAIL_TEST("SPDY_name_value_add failed\n"); - - SPDY_name_value_destroy(container); - - if(NULL == (container = SPDY_name_value_create ())) - { - FAIL_TEST("SPDY_name_value_create failed\n"); - } - - size = sizeof(pairs_with_empty)/sizeof(pairs_with_empty[0]); - - for(i=0;i<size; i+=2) - { - if(SPDY_YES != SPDY_name_value_add(container,pairs_with_empty[i],pairs_with_empty[i+1])) - { - FAIL_TEST("SPDY_name_value_add failed\n"); - } - value = SPDY_name_value_lookup(container,pairs_with_empty[i], &ret); - if(NULL == value || 1 != ret) - { - printf("%p; %i\n", value, ret); - FAIL_TEST("SPDY_name_value_lookup failed\n"); - } - } - - ret = SPDY_name_value_iterate(container, NULL, NULL); - if(SPDY_INPUT_ERROR != SPDY_name_value_add(container, "capitalLeter","anything") - || SPDY_name_value_iterate(container, NULL, NULL) != ret) - { - FAIL_TEST("SPDY_name_value_add failed\n"); - } - - SPDY_name_value_destroy(container); - - if(NULL == (container = SPDY_name_value_create ())) - { - FAIL_TEST("SPDY_name_value_create failed\n"); - } - - size = sizeof(pairs_with_dups)/sizeof(pairs_with_dups[0]); - - for(i=0;i<size; i+=2) - { - if(SPDY_YES != SPDY_name_value_add(container,pairs_with_dups[i],pairs_with_dups[i+1])) - { - FAIL_TEST("SPDY_name_value_add failed\n"); - } - } - - if(NULL == (container2 = SPDY_name_value_create ())) - { - FAIL_TEST("SPDY_name_value_create failed\n"); - } - - size2 = sizeof(pairs_different)/sizeof(pairs_different[0]); - - for(i=0;i<size2; i+=2) - { - if(SPDY_YES != SPDY_name_value_add(container2,pairs_different[i],pairs_different[i+1])) - { - FAIL_TEST("SPDY_name_value_add failed\n"); - } - } - - container_arr[0] = container; - container_arr[1] = container2; - if(0 > (ret = SPDYF_name_value_to_stream(container_arr, 2, &stream)) || NULL == stream) - FAIL_TEST("SPDYF_name_value_to_stream failed\n"); - ret = SPDYF_name_value_from_stream(stream, ret, &container3); - if(SPDY_YES != ret) - FAIL_TEST("SPDYF_name_value_from_stream failed\n"); - - if(SPDY_name_value_iterate(container3, NULL, NULL) - != (SPDY_name_value_iterate(container, NULL, NULL) + SPDY_name_value_iterate(container2, NULL, NULL))) - FAIL_TEST("SPDYF_name_value_from_stream failed\n"); - - for(i=size - 2; i >= 0; i-=2) - { - value = SPDY_name_value_lookup(container,pairs_with_dups[i], &ret); - if(NULL == value) - FAIL_TEST("SPDY_name_value_lookup failed\n"); - value2 = SPDY_name_value_lookup(container3,pairs_with_dups[i], &ret2); - if(NULL == value2) - FAIL_TEST("SPDY_name_value_lookup failed\n"); - - for(j=0; j<ret; ++j) - if(0 != strcmp(value2[j], value[j])) - FAIL_TEST("SPDY_name_value_lookup failed\n"); - } - for(i=size2 - 2; i >= 0; i-=2) - { - value = SPDY_name_value_lookup(container2,pairs_different[i], &ret); - if(NULL == value) - FAIL_TEST("SPDY_name_value_lookup failed\n"); - value2 = SPDY_name_value_lookup(container3,pairs_different[i], &ret2); - if(NULL == value2) - FAIL_TEST("SPDY_name_value_lookup failed\n"); - - for(j=0; j<ret; ++j) - if(0 != strcmp(value2[j], value[j])) - FAIL_TEST("SPDY_name_value_lookup failed\n"); - } - - SPDY_deinit(); - - return 0; -}