diff options
Diffstat (limited to 'http.h')
-rw-r--r-- | http.h | 719 |
1 files changed, 0 insertions, 719 deletions
diff --git a/http.h b/http.h deleted file mode 100644 index 9d6f9dd81da7..000000000000 --- a/http.h +++ /dev/null @@ -1,719 +0,0 @@ -/* ------------------------------------------------------------------------------- - Licensing information can be found at the end of the file. ------------------------------------------------------------------------------- - -http.hpp - v1.0 - Basic HTTP protocol implementation over sockets (no https). - -Do this: - #define HTTP_IMPLEMENTATION -before you include this file in *one* C/C++ file to create the implementation. -*/ - -#ifndef http_hpp -#define http_hpp - -#define _CRT_NONSTDC_NO_DEPRECATE -#define _CRT_SECURE_NO_WARNINGS -#include <stddef.h> // for size_t -#include <stdint.h> // for uintptr_t - -typedef enum http_status_t - { - HTTP_STATUS_PENDING, - HTTP_STATUS_COMPLETED, - HTTP_STATUS_FAILED, - } http_status_t; - -typedef struct http_t - { - http_status_t status; - int status_code; - char const* reason_phrase; - char const* content_type; - size_t response_size; - void* response_data; - } http_t; - -http_t* http_get( char const* url, void* memctx ); -http_t* http_post( char const* url, void const* data, size_t size, void* memctx ); - -http_status_t http_process( http_t* http ); - -void http_release( http_t* http ); - -#endif /* http_hpp */ - -/** - -http.hpp -======== - -Basic HTTP protocol implementation over sockets (no https). - - -Example -------- - - #define HTTP_IMPLEMENTATION - #include "http.h" - - int main( int argc, char** argv ) { - http_t* request = http_get( "http://www.mattiasgustavsson.com/http_test.txt", NULL ); - if( !request ) { - printf( "Invalid request.\n" ); - return 1; - } - - http_status_t status = HTTP_STATUS_PENDING; - int prev_size = -1; - while( status == HTTP_STATUS_PENDING ) { - status = http_process( request ); - if( prev_size != (int) request->response_size ) { - printf( "%d byte(s) received.\n", (int) request->response_size ); - prev_size = (int) request->response_size; - } - } - - if( status == HTTP_STATUS_FAILED ) { - printf( "HTTP request failed (%d): %s.\n", request->status_code, request->reason_phrase ); - http_release( request ); - return 1; - } - - printf( "\nContent type: %s\n\n%s\n", request->content_type, (char const*)request->response_data ); - http_release( request ); - return 0; - } - - -API Documentation ------------------ - -http.h is a small library for making http requests from a web server. It only supports GET and POST http commands, and -is designed for when you just need a very basic way of communicating over http. http.h does not support https -connections, just plain http. - -http.h is a single-header library, and does not need any .lib files or other binaries, or any build scripts. To use -it, you just include http.h to get the API declarations. To get the definitions, you must include http.h from -*one* single C or C++ file, and #define the symbol `HTTP_IMPLEMENTATION` before you do. - - -#### Custom memory allocators - -For working memory and to store the retrieved data, http.h needs to do dynamic allocation by calling `malloc`. Programs -might want to keep track of allocations done, or use custom defined pools to allocate memory from. http.h allows -for specifying custom memory allocation functions for `malloc` and `free`. This is done with the following code: - - #define HTTP_IMPLEMENTATION - #define HTTP_MALLOC( ctx, size ) ( my_custom_malloc( ctx, size ) ) - #define HTTP_FREE( ctx, ptr ) ( my_custom_free( ctx, ptr ) ) - #include "http.h" - -where `my_custom_malloc` and `my_custom_free` are your own memory allocation/deallocation functions. The `ctx` parameter -is an optional parameter of type `void*`. When `http_get` or `http_post` is called, , you can pass in a `memctx` -parameter, which can be a pointer to anything you like, and which will be passed through as the `ctx` parameter to every -`HTTP_MALLOC`/`HTTP_FREE` call. For example, if you are doing memory tracking, you can pass a pointer to your -tracking data as `memctx`, and in your custom allocation/deallocation function, you can cast the `ctx` param back to the -right type, and access the tracking data. - -If no custom allocator is defined, http.h will default to `malloc` and `free` from the C runtime library. - - -http_get --------- - - http_t* http_get( char const* url, void* memctx ) - -Initiates a http GET request with the specified url. `url` is a zero terminated string containing the request location, -just like you would type it in a browser, for example `http://www.mattiasgustavsson.com:80/http_test.txt`. `memctx` is a -pointer to user defined data which will be passed through to the custom HTTP_MALLOC/HTTP_FREE calls. It can be NULL if -no user defined data is needed. Returns a `http_t` instance, which needs to be passed to `http_process` to process the -request. When the request is finished (or have failed), the returned `http_t` instance needs to be released by calling -`http_release`. If the request was invalid, `http_get` returns NULL. - - -http_post ---------- - - http_t* http_post( char const* url, void const* data, size_t size, void* memctx ) - -Initiates a http POST request with the specified url. `url` is a zero terminated string containing the request location, -just like you would type it in a browser, for example `http://www.mattiasgustavsson.com:80/http_test.txt`. `data` is a -pointer to the data to be sent along as part of the request, and `size` is the number of bytes to send. `memctx` is a -pointer to user defined data which will be passed through to the custom HTTP_MALLOC/HTTP_FREE calls. It can be NULL if -no user defined data is needed. Returns a `http_t` instance, which needs to be passed to `http_process` to process the -request. When the request is finished (or have failed), the returned `http_t` instance needs to be released by calling -`http_release`. If the request was invalid, `http_post` returns NULL. - - -http_process ------------- - - http_status_t http_process( http_t* http ) - -http.h uses non-blocking sockets, so after a request have been made by calling either `http_get` or `http_post`, you -have to keep calling `http_process` for as long as it returns `HTTP_STATUS_PENDING`. You can call it from a loop which -does other work too, for example from inside a game loop or from a loop which calls `http_process` on multiple requests. -If the request fails, `http_process` returns `HTTP_STATUS_FAILED`, and the fields `status_code` and `reason_phrase` may -contain more details (for example, status code can be 404 if the requested resource was not found on the server). If the -request completes successfully, it returns `HTTP_STATUS_COMPLETED`. In this case, the `http_t` instance will contain -details about the result. `status_code` and `reason_phrase` contains the details about the result, as specified in the -HTTP protocol. `content_type` contains the MIME type for the returns resource, for example `text/html` for a normal web -page. `response_data` is the pointer to the received data, and `resonse_size` is the number of bytes it contains. In the -case when the response data is in text format, http.h ensures there is a zero terminator placed immediately after the -response data block, so it is safe to interpret the resonse data as a `char*`. Note that the data size in this case will -be the length of the data without the additional zero terminator. - - -http_release ------------- - - void http_release( http_t* http ) - -Releases the resources acquired by `http_get` or `http_post`. Should be call when you are finished with the request. - -*/ - -/* ----------------------- - IMPLEMENTATION ----------------------- -*/ - -#ifdef HTTP_IMPLEMENTATION - -#ifdef _WIN32 - #define _CRT_NONSTDC_NO_DEPRECATE - #define _CRT_SECURE_NO_WARNINGS - #pragma warning( push ) - #pragma warning( disable: 4127 ) // conditional expression is constant - #pragma warning( disable: 4255 ) // 'function' : no function prototype given: converting '()' to '(void)' - #pragma warning( disable: 4365 ) // 'action' : conversion from 'type_1' to 'type_2', signed/unsigned mismatch - #pragma warning( disable: 4574 ) // 'Identifier' is defined to be '0': did you mean to use '#if identifier'? - #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directive' - #pragma warning( disable: 4706 ) // assignment within conditional expression - #include <winsock2.h> - #include <ws2tcpip.h> - #pragma warning( pop ) - #pragma comment (lib, "Ws2_32.lib") - #include <string.h> - #include <stdio.h> - #define HTTP_SOCKET SOCKET - #define HTTP_INVALID_SOCKET INVALID_SOCKET -#else - #include <stdlib.h> - #include <stdio.h> - #include <string.h> - #include <sys/types.h> - #include <sys/socket.h> - #include <unistd.h> - #include <errno.h> - #include <fcntl.h> - #include <netdb.h> - #define HTTP_SOCKET int - #define HTTP_INVALID_SOCKET -1 -#endif - -#ifndef HTTP_MALLOC - #define _CRT_NONSTDC_NO_DEPRECATE - #define _CRT_SECURE_NO_WARNINGS - #include <stdlib.h> - #define HTTP_MALLOC( ctx, size ) ( malloc( size ) ) - #define HTTP_FREE( ctx, ptr ) ( free( ptr ) ) -#endif - -typedef struct http_internal_t - { - /* keep this at the top!*/ - http_t http; - /* because http_internal_t* can be cast to http_t*. */ - - void* memctx; - HTTP_SOCKET socket; - int connect_pending; - int request_sent; - char address[ 256 ]; - char request_header[ 256 ]; - char* request_header_large; - void* request_data; - size_t request_data_size; - char reason_phrase[ 1024 ]; - char content_type[ 256 ]; - size_t data_size; - size_t data_capacity; - void* data; - } http_internal_t; - - -static int http_internal_parse_url( char const* url, char* address, size_t address_capacity, char* port, - size_t port_capacity, char const** resource ) - { - // make sure url starts with http:// - if( strncmp( url, "http://", 7 ) != 0 ) return 0; - url += 7; // skip http:// part of url - - size_t url_len = strlen( url ); - - // find end of address part of url - char const* address_end = strchr( url, ':' ); - if( !address_end ) address_end = strchr( url, '/' ); - if( !address_end ) address_end = url + url_len; - - // extract address - size_t address_len = (size_t)( address_end - url ); - if( address_len >= address_capacity ) return 0; - memcpy( address, url, address_len ); - address[ address_len ] = 0; - - // check if there's a port defined - char const* port_end = address_end; - if( *address_end == ':' ) - { - ++address_end; - port_end = strchr( address_end, '/' ); - if( !port_end ) port_end = address_end + strlen( address_end ); - size_t port_len = (size_t)( port_end - address_end ); - if( port_len >= port_capacity ) return 0; - memcpy( port, address_end, port_len ); - port[ port_len ] = 0; - } - else - { - // use default port number 80 - if( port_capacity <= 2 ) return 0; - strcpy( port, "80" ); - } - - - *resource = port_end; - - return 1; - } - - -HTTP_SOCKET http_internal_connect( char const* address, char const* port ) - { - // set up hints for getaddrinfo - struct addrinfo hints; - memset( &hints, 0, sizeof( hints ) ); - hints.ai_family = AF_UNSPEC; // the Internet Protocol version 4 (IPv4) address family. - hints.ai_flags = AI_PASSIVE; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; // Use Transmission Control Protocol (TCP). - - // resolve the server address and port - struct addrinfo* addri = 0; - int error = getaddrinfo( address, port, &hints, &addri) ; - if( error != 0 ) return HTTP_INVALID_SOCKET; - - // create the socket - HTTP_SOCKET sock = socket( addri->ai_family, addri->ai_socktype, addri->ai_protocol ); - if( sock == -1) - { - freeaddrinfo( addri ); - return HTTP_INVALID_SOCKET; - } - - // set socket to nonblocking mode - u_long nonblocking = 1; - #ifdef _WIN32 - int res = ioctlsocket( sock, FIONBIO, &nonblocking ); - #else - int flags = fcntl( sock, F_GETFL, 0 ); - int res = fcntl( sock, F_SETFL, flags | O_NONBLOCK ); - #endif - if( res == -1 ) - { - freeaddrinfo( addri ); - #ifdef _WIN32 - closesocket( sock ); - #else - close( sock ); - #endif - return HTTP_INVALID_SOCKET; - } - - // connect to server - if( connect( sock, addri->ai_addr, (int)addri->ai_addrlen ) == -1 ) - { - #ifdef _WIN32 - if( WSAGetLastError() != WSAEWOULDBLOCK && WSAGetLastError() != WSAEINPROGRESS ) - { - freeaddrinfo( addri ); - closesocket( sock ); - return HTTP_INVALID_SOCKET; - } - #else - if( errno != EWOULDBLOCK && errno != EINPROGRESS && errno != EAGAIN ) - { - freeaddrinfo( addri ); - close( sock ); - return HTTP_INVALID_SOCKET; - } - #endif - } - - freeaddrinfo( addri ); - return sock; - } - - -static http_internal_t* http_internal_create( size_t request_data_size, void* memctx ) - { - http_internal_t* internal = (http_internal_t*) HTTP_MALLOC( memctx, sizeof( http_internal_t ) + request_data_size ); - - internal->http.status = HTTP_STATUS_PENDING; - internal->http.status_code = 0; - internal->http.response_size = 0; - internal->http.response_data = NULL; - - internal->memctx = memctx; - internal->connect_pending = 1; - internal->request_sent = 0; - - strcpy( internal->reason_phrase, "" ); - internal->http.reason_phrase = internal->reason_phrase; - - strcpy( internal->content_type, "" ); - internal->http.content_type = internal->content_type; - - internal->data_size = 0; - internal->data_capacity = 64 * 1024; - internal->data = HTTP_MALLOC( memctx, internal->data_capacity ); - - internal->request_data = NULL; - internal->request_data_size = 0; - - return internal; - } - - -http_t* http_get( char const* url, void* memctx ) - { - #ifdef _WIN32 - WSADATA wsa_data; - if( WSAStartup( MAKEWORD( 1, 0 ), &wsa_data ) != 0 ) return NULL; - #endif - - char address[ 256 ]; - char port[ 16 ]; - char const* resource; - - if( http_internal_parse_url( url, address, sizeof( address ), port, sizeof( port ), &resource ) == 0 ) - return NULL; - - HTTP_SOCKET socket = http_internal_connect( address, port ); - if( socket == HTTP_INVALID_SOCKET ) return NULL; - - http_internal_t* internal = http_internal_create( 0, memctx ); - internal->socket = socket; - - char* request_header; - size_t request_header_len = 64 + strlen( resource ) + strlen( address ) + strlen( port ); - if( request_header_len < sizeof( internal->request_header ) ) - { - internal->request_header_large = NULL; - request_header = internal->request_header; - } - else - { - internal->request_header_large = (char*) HTTP_MALLOC( memctx, request_header_len + 1 ); - request_header = internal->request_header_large; - } - int default_http_port = (strcmp(port, "80") == 0); - sprintf( request_header, "GET %s HTTP/1.0\r\nHost: %s%s%s\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port ); - - return &internal->http; - } - - -http_t* http_post( char const* url, void const* data, size_t size, void* memctx ) - { - #ifdef _WIN32 - WSADATA wsa_data; - if( WSAStartup( MAKEWORD( 1, 0 ), &wsa_data ) != 0 ) return 0; - #endif - - char address[ 256 ]; - char port[ 16 ]; - char const* resource; - - if( http_internal_parse_url( url, address, sizeof( address ), port, sizeof( port ), &resource ) == 0 ) - return NULL; - - HTTP_SOCKET socket = http_internal_connect( address, port ); - if( socket == HTTP_INVALID_SOCKET ) return NULL; - - http_internal_t* internal = http_internal_create( size, memctx ); - internal->socket = socket; - - char* request_header; - size_t request_header_len = 64 + strlen( resource ) + strlen( address ) + strlen( port ); - if( request_header_len < sizeof( internal->request_header ) ) - { - internal->request_header_large = NULL; - request_header = internal->request_header; - } - else - { - internal->request_header_large = (char*) HTTP_MALLOC( memctx, request_header_len + 1 ); - request_header = internal->request_header_large; - } - int default_http_port = (strcmp(port, "80") == 0); - sprintf( request_header, "POST %s HTTP/1.0\r\nHost: %s%s%s\r\nContent-Length: %d\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port, - (int) size ); - - internal->request_data_size = size; - internal->request_data = ( internal + 1 ); - memcpy( internal->request_data, data, size ); - - return &internal->http; - } - - -http_status_t http_process( http_t* http ) - { - http_internal_t* internal = (http_internal_t*) http; - - if( http->status == HTTP_STATUS_FAILED ) return http->status; - - if( internal->connect_pending ) - { - fd_set sockets_to_check; - FD_ZERO( &sockets_to_check ); - #pragma warning( push ) - #pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect - FD_SET( internal->socket, &sockets_to_check ); - #pragma warning( pop ) - struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; - // check if socket is ready for send - if( select( (int)( internal->socket + 1 ), NULL, &sockets_to_check, NULL, &timeout ) == 1 ) - { - int opt = -1; - socklen_t len = sizeof( opt ); - if( getsockopt( internal->socket, SOL_SOCKET, SO_ERROR, (char*)( &opt ), &len) >= 0 && opt == 0 ) - internal->connect_pending = 0; // if it is, we're connected - } - } - - if( internal->connect_pending ) return http->status; - - if( !internal->request_sent ) - { - char const* request_header = internal->request_header_large ? - internal->request_header_large : internal->request_header; - if( send( internal->socket, request_header, (int) strlen( request_header ), 0 ) == -1 ) - { - http->status = HTTP_STATUS_FAILED; - return http->status; - } - if( internal->request_data_size ) - { - int res = send( internal->socket, (char const*)internal->request_data, (int) internal->request_data_size, 0 ); - if( res == -1 ) - { - http->status = HTTP_STATUS_FAILED; - return http->status; - } - } - internal->request_sent = 1; - return http->status; - } - - // check if socket is ready for recv - fd_set sockets_to_check; - FD_ZERO( &sockets_to_check ); - #pragma warning( push ) - #pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect - FD_SET( internal->socket, &sockets_to_check ); - #pragma warning( pop ) - struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; - while( select( (int)( internal->socket + 1 ), &sockets_to_check, NULL, NULL, &timeout ) == 1 ) - { - char buffer[ 4096 ]; - int size = recv( internal->socket, buffer, sizeof( buffer ), 0 ); - if( size == -1 ) - { - http->status = HTTP_STATUS_FAILED; - return http->status; - } - else if( size > 0 ) - { - size_t min_size = internal->data_size + size + 1; - if( internal->data_capacity < min_size ) - { - internal->data_capacity *= 2; - if( internal->data_capacity < min_size ) internal->data_capacity = min_size; - void* new_data = HTTP_MALLOC( memctx, internal->data_capacity ); - memcpy( new_data, internal->data, internal->data_size ); - HTTP_FREE( memctx, internal->data ); - internal->data = new_data; - } - memcpy( (void*)( ( (uintptr_t) internal->data ) + internal->data_size ), buffer, (size_t) size ); - internal->data_size += size; - } - else if( size == 0 ) - { - char const* status_line = (char const*) internal->data; - - int header_size = 0; - char const* header_end = strstr( status_line, "\r\n\r\n" ); - if( header_end ) - { - header_end += 4; - header_size = (int)( header_end - status_line ); - } - else - { - http->status = HTTP_STATUS_FAILED; - return http->status; - } - - // skip http version - status_line = strchr( status_line, ' ' ); - if( !status_line ) - { - http->status = HTTP_STATUS_FAILED; - return http->status; - } - ++status_line; - - // extract status code - char status_code[ 16 ]; - char const* status_code_end = strchr( status_line, ' ' ); - if( !status_code_end ) - { - http->status = HTTP_STATUS_FAILED; - return http->status; - } - memcpy( status_code, status_line, (size_t)( status_code_end - status_line ) ); - status_code[ status_code_end - status_line ] = 0; - status_line = status_code_end + 1; - http->status_code = atoi( status_code ); - - // extract reason phrase - char const* reason_phrase_end = strstr( status_line, "\r\n" ); - if( !reason_phrase_end ) - { - http->status = HTTP_STATUS_FAILED; - return http->status; - } - size_t reason_phrase_len = (size_t)( reason_phrase_end - status_line ); - if( reason_phrase_len >= sizeof( internal->reason_phrase ) ) - reason_phrase_len = sizeof( internal->reason_phrase ) - 1; - memcpy( internal->reason_phrase, status_line, reason_phrase_len ); - internal->reason_phrase[ reason_phrase_len ] = 0; - status_line = reason_phrase_end + 1; - - // extract content type - char const* content_type_start = strstr( status_line, "Content-Type: " ); - if( content_type_start ) - { - content_type_start += strlen( "Content-Type: " ); - char const* content_type_end = strstr( content_type_start, "\r\n" ); - if( content_type_end ) - { - size_t content_type_len = (size_t)( content_type_end - content_type_start ); - if( content_type_len >= sizeof( internal->content_type ) ) - content_type_len = sizeof( internal->content_type ) - 1; - memcpy( internal->content_type, content_type_start, content_type_len ); - internal->content_type[ content_type_len ] = 0; - } - } - - http->status = http->status_code < 300 ? HTTP_STATUS_COMPLETED : HTTP_STATUS_FAILED; - http->response_data = (void*)( ( (uintptr_t) internal->data ) + header_size ); - http->response_size = internal->data_size - header_size; - - // add an extra zero after the received data, but don't modify the size, so ascii results can be used as - // a zero terminated string. the size returned will be the string without this extra zero terminator. - ( (char*)http->response_data )[ http->response_size ] = 0; - return http->status; - } - } - - return http->status; - } - - -void http_release( http_t* http ) - { - http_internal_t* internal = (http_internal_t*) http; - #ifdef _WIN32 - closesocket( internal->socket ); - #else - close( internal->socket ); - #endif - - if( internal->request_header_large) HTTP_FREE( memctx, internal->request_header_large ); - HTTP_FREE( memctx, internal->data ); - HTTP_FREE( memctx, internal ); - #ifdef _WIN32 - WSACleanup(); - #endif - } - - -#endif /* HTTP_IMPLEMENTATION */ - -/* -revision history: - 1.0 first released version -*/ - -/* ------------------------------------------------------------------------------- - -This software is available under 2 licenses - you may choose the one you like. - ------------------------------------------------------------------------------- - -ALTERNATIVE A - MIT License - -Copyright (c) 2016 Mattias Gustavsson - -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. - ------------------------------------------------------------------------------- - -ALTERNATIVE B - Public Domain (www.unlicense.org) - -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. - -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. - -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 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. - ------------------------------------------------------------------------------- -*/ |