Gå til innhold

noen som er interesserte i et lite oppdrag?


Anbefalte innlegg

hei,

jeg kunne trengt en HTTP-server skrevet i C

 

edit: ..å si "server" blir kanskje litt galt; da den mer er en slags proxy eller "oversetter"

 

kort (ikke ferdig) beskrivelse her:

http://nostdal.org:6060/~lars/programming/...aromyxo/com.txt

 

..og litt av implementasjonen her:

http://nostdal.org:6060/~lars/programming/...xo/com/socket.c

 

noen som kan hjelpe?

Endret av dayslepr
Lenke til kommentar
Videoannonse
Annonse
hei,

jeg kunne trengt en HTTP-server skrevet i C

 

edit: ..å si "server" blir kanskje litt galt; da den mer er en slags proxy eller "oversetter"

 

kort (ikke ferdig) beskrivelse her:

http://nostdal.org:6060/~lars/programming/...aromyxo/com.txt

 

..og litt av implementasjonen her:

http://nostdal.org:6060/~lars/programming/...xo/com/socket.c

 

noen som kan hjelpe?

5857177[/snapback]

 

Legg de filene på en server som kjører på port 80 eller noe, ellers kommer jeg ikke til dem på jobben :)

Lenke til kommentar

okei :)

Aromyxo - Com

=============

 

Basically a lightweight HTTP-server written in C for maximum speed and

portability that:

 

 

  * Reads HTTP-requests.

  * Converts HTTP-messages to a given format.

  * Calls a foreign function (implemented as a C callback) with a pointer to

    the converted data, that generates a response in the same format.

  * Converts the response returned from the foreign function back to a "regular"

    HTTP-message.

  * Sends the "decoded" response back to the client.

 

 

This HTTP-request:

 

 

  GET / HTTP/1.1\r\n

  Host: symbolicweb.org\r\n

  Connection: close\r\n

  Accept-Encoding: gzip\r\n\r\n

 

 

..could for instance be converted to a string like this:

 

 

  ((method . get) (url . "/") (version . "1.1") (connection . close)

  (accept-encoding "gzip"))

 

 

The point being that this can be read directly in Lisp by the function `read',

giving you the data-structure to play with. In other languages one have stuff

like `eval' that does the same thing.

 

Since `method' only can have a finite set of values, it's value is not formatted

as a string. The same goes for headers like `content-length'. The body of the

request (if any) should be put in an association with the key-name `body'.

 

Fur chunked transfers, it should first send the headers - then call a foreign

function for each chunk.

 

I'm thinking this thing could be extensible by not having the format hardcoded,

but instead use #define-macros for the syntax-parts it is to use. Like for

instance:

 

 

  #define HEADERS_START "(("

  #define HEADERS_END "))"

  #define HEADER_START '('

  #define HEADER_END ')'

  #define KEY_VALUE_DELIMITER '.'

  etc.

 

 

There are other ways to achieve extensibility like this; this is just a

suggestion.

 

 

 

Suggestions

===========

 

Simple unithreaded at first - then multithreading with pools?

 

It must run under Linux, and should be written so it can be ported later.

 

/*
  gcc -Wall -shared -fPIC -g socket.c -o libaromyxo.so
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>


#define ESTIMATED_NUMBER_OF_CONNECTIONS 100
#define MAX_EVENTS 100
// how much extra to allocate when buffer is full.
#define RECV_BUFFER_STEP_SIZE 5


int amTCPServerSocket(int port, int backlog)
{
 int s;
 if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
   perror("amTCPServerSocket, socket");
   return -1;
 }

 fcntl(s, F_SETFL, O_NONBLOCK);

 int yes = 1;
 if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
   perror("setsockopt");
   return -1;
 } 
 
 struct sockaddr_in my_addr;
 my_addr.sin_family = AF_INET;  
 my_addr.sin_port = htons(port);
 my_addr.sin_addr.s_addr = INADDR_ANY;
 memset(&(my_addr.sin_zero), '\0', 8);

 if(bind(s, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
   perror("amTCPServerSocket, bind");
   return -1;
 }
 
 if(listen(s, backlog) == -1) {
   perror("amTCPServerSocket, listen");
   return -1;
 }
 return s;  
} // amTCPServerSocket


inline int amAccept(int socket)
{
 // TODO: Read address here - or do it later?
 int sd = accept(socket, NULL, 0);
 if(sd == -1)
   perror("amAccept, accept");
 return sd;
} // amAccept


typedef struct {
 int socket;
 char status;
 unsigned int size;
 void* data;
} DataBlock;


inline DataBlock* amRecvUntilWouldBlock(int socket)
    /*
      Recieves data from socket until reads would block (EAGAIN).
      NOTE: This function returns malloced data that needs to be freed.
     */
{
 unsigned int read_until_now = 0;
 int read;
 DataBlock* data_block = malloc(sizeof(DataBlock));
 data_block->socket = socket;
 unsigned int remaining = RECV_BUFFER_STEP_SIZE * 2;
 data_block->data = malloc((sizeof(char) * RECV_BUFFER_STEP_SIZE * 2));

 //printf("amRecvUntilWouldBlock, start - will read from socket: %d\n", socket);
 while((read = recv(socket, data_block->data + read_until_now, RECV_BUFFER_STEP_SIZE, 0)) > 0) {
   read_until_now += read;
   remaining -= read;

   printf("amRecvUntilWouldBlock, socket: %d, read: %d, read_until_now: %d, remaining: %d\n", socket, read, read_until_now, remaining);
     
   if(remaining <= (RECV_BUFFER_STEP_SIZE / 2) - 1) {
     data_block->data = realloc(data_block->data, sizeof(char) * (read_until_now + remaining + RECV_BUFFER_STEP_SIZE));
     remaining = remaining + RECV_BUFFER_STEP_SIZE;
     printf("amRecvUntilWouldBlock, reallocating, new space remaining: %d\n", remaining);
   }
 }
 data_block->status = read;
 data_block->size = read_until_now;
 //printf("amRecvUntilWouldBlock, status: %d\n", read);
 //perror("amRecvUntilWouldBlock, perror: ");
 //printf("amRecvUntilWouldBlock, end\n");
 return data_block;
} // amRecvUntilWouldBlock


typedef void (*AMDispatcherForeign)(DataBlock** data_blocks, unsigned int num);


volatile char am_listening = 0;


int amDispatcherC(int server_socket, AMDispatcherLisp amDispatcherLisp)
{
 int n;
 unsigned int n_active;
 int epfd = epoll_create(ESTIMATED_NUMBER_OF_CONNECTIONS);
 int incoming_socket;
 int nfds;
 struct epoll_event ev;
 struct epoll_event events[MAX_EVENTS];
 DataBlock* data_blocks[MAX_EVENTS];

 ev.events = EPOLLIN | EPOLLPRI;
 ev.data.fd = server_socket;
 if(epoll_ctl(epfd, EPOLL_CTL_ADD, server_socket, &ev) == -1) {
   perror("amDispatcherC, epoll_ctl (server_socket)");
   return errno;
 }
 
 am_listening = 1;
 
 while(am_listening) {
   n_active = 0;
   if((nfds = epoll_wait(epfd, events, MAX_EVENTS, -1)) == -1) {
     perror("amDispatcherC, epoll_wait");
     return errno;
   }
   
   for(n = 0; n < nfds; ++n) {
     if(events[n].data.fd == server_socket) {
       // New incoming connection on listening socket //
       if((incoming_socket = amAccept(server_socket)) == -1) {
         // TODO: Some other way of letting the Lisp-side know about this?
         perror("amDispatcherC, amAccept (incoming_socket)");
         continue;
       }

       fcntl(incoming_socket, F_SETFL, O_NONBLOCK);
       ev.events = EPOLLIN | EPOLLPRI | EPOLLET;
       ev.data.fd = incoming_socket;
       if(epoll_ctl(epfd, EPOLL_CTL_ADD, incoming_socket, &ev) == -1) {
         perror("amDispatcherC, epoll_ctl (incoming_socket)");
         return errno;
       }
     }
     else {
       // Activity on an already connected socket //
       data_blocks[n_active++] = amRecvUntilWouldBlock(events[n].data.fd);
       //printf("amDispatcherC will dispatch socket %d\n", events[n].data.fd);
     }
   }
   printf("callback!\n");
   amDispatcherLisp(data_blocks, n_active);
   // Clean up after Lisp-side is done with data //
   int i;
   for(i = 0; i < n_active; i++) {
     free(data_blocks[i]->data);
     free(data_blocks[i]);
   }
 }
 return 0;
} // amDispatcherC

Endret av dayslepr
Lenke til kommentar
  • 2 uker senere...

jau - noe sånnt

 

men jeg har lagt denne idéen på hylla intil videre; det blir litt mer styr m.t.p. encoding og annet som jeg ikke gidder å bruke tid på nå (edit: eller, uhm - encoding løste seg visst noen strakser etter jeg skrev denne, men les neste "edit:")

 

tror uansett det er mulig å få til det jeg er ute etter uten denne tingen

 

edit:

eller med nærmere ettertanke tror jeg dette er en premature optimization ..

 

så fort jeg får ferdig litt andre ting først, ordner jeg denne optimaliseringen selv.. jeg tror jeg må gjøre det selv (for å bli fornøyd?) uansett *eeep* :tease:

 

...alle "rister på hue" og lurer på hvorfor jeg vil dette (hvor ting ender langt frem i tid?); så jeg "får vel vise dem" - da forklaringen(e) vil bli lengre enn selve koden alikevell (hint: >10k klienter, massiv multitasking med portabelt GUI-frontend)

Endret av dayslepr
Lenke til kommentar

Da har jeg så småning om (er det det de sier i sverige?) begynnt på prosjektet:

 

http://nostdal.org/sw/wiki/EPServer

 

det ser ut til at jeg gjør det litt lettere enn jeg hadde tenkt å gjøre det i utgangspunktet; jeg sender i stedet socketene over til foreign-siden (Lisp i mitt tilfele) og leser/parser ting der .. så kan jeg heller porte den "parse-biten" til C siden .. det viktigste er at jeg har en veldig skalerbar "event-basert async-server-ting-sak", og det får jeg .... :)

Endret av dayslepr
Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
×
×
  • Opprett ny...