📄 ilibwebclient.c
字号:
// Free the resources associated with the header // ILibDestructPacket(wcdo->header); wcdo->header = NULL; } // // Reset all the flags // ILibWebClient_ResetWCDO(wcdo);}/// <summary>/// Internal method used to free resources associated with a WebRequest/// </summary>/// <param name="wr">The WebRequest to free</param>static void ILibWebClient_DestroyWebRequest(struct ILibWebRequest *wr){ int i; for(i=0;i<wr->NumberOfBuffers;++i) { // // If we own any of the buffers, we need to free them // if(wr->UserFree[i]==0) {free(wr->Buffer[i]);} } // // Free the other resources // free(wr->Buffer); free(wr->BufferLength); free(wr->UserFree); free(wr);}/// <summary>/// Internal method called when a WebClient has finished processing a Request/Response/// </summary>/// <param name="socketModule">The WebClient</param>/// <param name="wcdo">The associated WebClientDataObject</param>static void ILibWebClient_FinishedResponse(void *socketModule, struct ILibWebClientDataObject *wcdo){ struct ILibWebRequest *wr; int i; if(wcdo==NULL) {return;} // // Only continue if this is a client calling this // if(wcdo->Server!=0) {return;}//{{{ REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT--> }}} if(wcdo->chunk!=NULL) { // // Free any resources associated with the Chunk Processor // free(wcdo->chunk->buffer); free(wcdo->chunk); wcdo->chunk = NULL; }//{{{ <--REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT }}} if(wcdo->header!=NULL) { // // Free any resources associated with the header // ILibDestructPacket(wcdo->header); wcdo->header = NULL; } // // Reset the flags // ILibWebClient_ResetWCDO(wcdo); SEM_TRACK(WebClient_TrackLock("ILibWebClient_FinishedResponse",1,wcdo->Parent);) wr = ILibQueue_DeQueue(wcdo->RequestQueue); ILibWebClient_DestroyWebRequest(wr); wr = ILibQueue_PeekQueue(wcdo->RequestQueue); if(wr==NULL) { // // Since the request queue is empty, that means this connection is now idle. // Set a timed callback, so we can free this resource if neccessary // if(ILibIsChainBeingDestroyed(wcdo->Parent->Chain)==0) { ILibLifeTime_Add(wcdo->Parent->timer,wcdo,HTTP_SESSION_IDLE_TIMEOUT,&ILibWebClient_TimerSink,&ILibWebClient_TimerInterruptSink); } } SEM_TRACK(WebClient_TrackUnLock("ILibWebClient_FinishedResponse",2,wcdo->Parent);)//{{{ REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT--> }}} if(wr!=NULL) { // // There are still pending requests in the queue, so try to send them // if(wcdo->PipelineFlag!=PIPELINE_NO) { // // If the connection is still open, and we didn't flag this as not supporting // persistent connections, than obviously it is supported // wcdo->PipelineFlag = PIPELINE_YES; for(i=0;i<wr->NumberOfBuffers;++i) { // // Try to send the request // ILibAsyncSocket_Send(wcdo->SOCK,wr->Buffer[i],wr->BufferLength[i],ILibAsyncSocket_MemoryOwnership_STATIC); } } } if(wcdo->PipelineFlag==PIPELINE_NO) {//{{{ <--REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT }}} /* Pipelining is not supported, so we should just close the socket, instead of waiting for the other guy to close it, because if they forget to, it will screw us over if there are pending requests */ // // It should also be noted, that when this closes, the module will realize there are // pending requests, in which case it will open a new connection for the requests. // if(ILibIsChainBeingDestroyed(wcdo->Parent->Chain)==0) { // // Only do this if the chain is still alive, otherwise things will get screwed // up, because modules may not be ready. // ILibAsyncSocket_Disconnect(wcdo->SOCK); }//{{{ REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT--> }}} }//{{{ <--REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT }}}}//{{{ REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT--> }}}/// <summary>/// Internal method called to decode chunked transfers/// </summary>/// <param name="socketModule">The ILibWebClient token</param>/// <param name="wcdo">The associated WebClientDataObject</param>/// <param name="buffer">The receive buffer</param>/// <param name="p_beginPointer">The buffer start pointer</param>/// <param name="endPointer">The length of the buffer</param>static void ILibWebClient_ProcessChunk(void *socketModule, struct ILibWebClientDataObject *wcdo, char *buffer, int *p_beginPointer, int endPointer){ char *hex; int i; struct parser_result *pr; struct ILibWebRequest *wr; int bp; int resize; if(wcdo==NULL) {return;} // // ToDo: Document what a Parent is // if(wcdo->Parent!=NULL) { SEM_TRACK(WebClient_TrackLock("ILibWebClient_ProcessChunk",1,wcdo->Parent);) } wr = ILibQueue_PeekQueue(wcdo->RequestQueue); if(wcdo->Parent!=NULL) { SEM_TRACK(WebClient_TrackUnLock("ILibWebClient_ProcessChunk",2,wcdo->Parent);) } if(wcdo->chunk==NULL) { // // Create a state object for the Chunk Processor // wcdo->chunk = (struct ILibWebClient_ChunkData*)malloc(sizeof(struct ILibWebClient_ChunkData)); memset(wcdo->chunk,0,sizeof(struct ILibWebClient_ChunkData)); wcdo->chunk->buffer = (char*)malloc(INITIAL_BUFFER_SIZE); wcdo->chunk->mallocSize = INITIAL_BUFFER_SIZE; } switch(wcdo->chunk->Flag) { // // Based on the Chunk Flag, we can figure out how to parse this thing // case STARTCHUNK: // Reading Chunk Header if(endPointer<3){return;} for(i=2;i<endPointer;++i) { if(buffer[i-2]=='\r' && buffer[i-1]=='\n') { // // The chunk header is terminated with a CRLF. The part before the ';' // is the hex number representing the length of the chunk // pr = ILibParseString(buffer,0,i-2,";",1); pr->FirstResult->data[pr->FirstResult->datalength] = '\0'; wcdo->chunk->bytesLeft = (int)strtol(pr->FirstResult->data,&hex,16); *p_beginPointer = i; wcdo->chunk->Flag=wcdo->chunk->bytesLeft==0?FOOTERCHUNK:DATACHUNK; ILibDestructParserResults(pr); break; } } break; case ENDCHUNK: if(endPointer>=2) { // // There is more chunks to come // *p_beginPointer = 2; wcdo->chunk->Flag = STARTCHUNK; } break; case DATACHUNK: if(endPointer>=wcdo->chunk->bytesLeft) { // // Only consume what we need // wcdo->chunk->Flag = ENDCHUNK; i = wcdo->chunk->bytesLeft; } else { // // Consume all of the data // i=endPointer; } if(wcdo->chunk->offset+endPointer>wcdo->chunk->mallocSize) { // // The buffer is too small, we need to make it bigger // ToDo: Add code to enforce a max buffer size if specified // ToDo: Does the above layer need to know when buffers were realloced? // resize = wcdo->chunk->offset+endPointer-wcdo->chunk->mallocSize>INITIAL_BUFFER_SIZE?wcdo->chunk->offset+endPointer-wcdo->chunk->mallocSize:INITIAL_BUFFER_SIZE; wcdo->chunk->buffer = (char*)realloc(wcdo->chunk->buffer,wcdo->chunk->mallocSize+resize); wcdo->chunk->mallocSize += resize; } // // Write the decoded chunk blob into the buffer // memcpy(wcdo->chunk->buffer+wcdo->chunk->offset,buffer,i); MEMCHECK(assert(wcdo->chunk->offset+i<=wcdo->chunk->mallocSize);) // // Adjust our counters // wcdo->chunk->bytesLeft-=i; wcdo->chunk->offset+=i; bp = 0; if(wr!=NULL && wr->OnResponse!=NULL) { // // Let the user know we got some data // wr->OnResponse( wcdo->SOCK, 0, wcdo->header, wcdo->chunk->buffer, &bp, wcdo->chunk->offset, 0, wr->user1, wr->user2, &(wcdo->PAUSE)); } if(bp==wcdo->chunk->offset) { // // The user consumed all of the data, so we can recycle the buffer // wcdo->chunk->offset = 0; } else if(bp!=0) { // // The user consumed part of the data, so we need to shift the buffers // memcpy(wcdo->chunk->buffer,wcdo->chunk->buffer+bp,wcdo->chunk->offset-bp); MEMCHECK(assert(i<=wcdo->chunk->mallocSize);) wcdo->chunk->offset -= bp; } *p_beginPointer = i; break; case FOOTERCHUNK: if(endPointer>=2) { for(i=2;i<=endPointer;++i) { if(buffer[i-2]=='\r' && buffer[i-1]=='\n') { // // An empty line means the chunk is finished // if(i==2) { // FINISHED if(wr != NULL && wr->OnResponse!=NULL) { wr->OnResponse( wcdo->SOCK, 0, wcdo->header, wcdo->chunk->buffer, p_beginPointer, wcdo->chunk->offset, -1, wr->user1, wr->user2, &(wcdo->PAUSE)); } if(ILibAsyncSocket_IsFree(socketModule)==0) { // // Free the resources associated with this chunk // if(wcdo->chunk!=NULL) { if(wcdo->chunk->buffer!=NULL) {free(wcdo->chunk->buffer);} free(wcdo->chunk); wcdo->chunk = NULL; } ILibWebClient_FinishedResponse(wcdo->SOCK,wcdo); } *p_beginPointer = 2; } else { // // ToDo: This is where to add code to add support trailers // } } } } break; }}//{{{ <--REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT }}}/// <summary>/// Internal method dispatched by the OnData event of the underlying ILibAsyncSocket/// </summary>/// <param name="socketModule">The underlying ILibAsyncSocket</param>/// <param name="buffer">The receive buffer</param>/// <param name="p_beginPointer">start pointer in the buffer</param>/// <param name="endPointer">The length of the buffer</param>/// <param name="InterruptPtr">Function Pointer that triggers when a connection is interrupted</param>/// <param name="user">User data that can be set/received</param>/// <param name="PAUSE">Flag to tell the underlying socket to pause reading data</param>void ILibWebClient_OnData(void* socketModule,char* buffer,int *p_beginPointer, int endPointer,void (**InterruptPtr)(void *socketModule, void *user), void **user, int *PAUSE){ struct ILibWebClientDataObject *wcdo = (struct ILibWebClientDataObject*)(*user); struct ILibWebRequest *wr; struct packetheader *tph; struct packetheader_field_node *phfn; int i=0; int zero = 0; int Fini; if(wcdo==NULL) {return;} if(wcdo->Server==0) { SEM_TRACK(WebClient_TrackLock("ILibWebClient_OnData",1,wcdo->Parent);) } wr = (struct ILibWebRequest*)ILibQueue_PeekQueue(wcdo->RequestQueue); if(wcdo->Server==0) { SEM_TRACK(WebClient_TrackUnLock("ILibWebClient_OnData",2,wcdo->Parent);) } if(wr==NULL) { // // There are no pending requests, so we have no idea what we are supposed to do with // this data, other than just recycling the receive buffer, so we don't leak memory. // If this code executes, this usually signifies a processing error of some sort. Most // of the time, it means the remote endpoint is sending invalid packets. // *p_beginPointer = endPointer; return; } if(wcdo->FinHeader==0) { //Still Reading Headers if(endPointer - (*p_beginPointer)>=4) { while(i <= (endPointer - (*p_beginPointer))-4) { if(buffer[*p_beginPointer+i]=='\r' && buffer[*p_beginPointer+i+1]=='\n' && buffer[*p_beginPointer+i+2]=='\r' && buffer[*p_beginPointer+i+3]=='\n') { // // Headers are delineated with a CRLF, and terminated with an empty line // wcdo->HeaderLength = i+4; wcdo->WaitForClose=1; wcdo->BytesLeft=-1; wcdo->FinHeader=1; wcdo->header = ILibParsePacketHeader(buffer,*p_beginPointer,endPointer-(*p_beginPointer)); if(wcdo->header!=NULL) { wcdo->header->ReceivingAddress = wcdo->LocalIP; // // Introspect Request, to see what to do next // phfn = wcdo->header->FirstField; while(phfn!=NULL) {//{{{ REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT--> }}} if(phfn->FieldLength==6 && strncasecmp(phfn->Field,"expect",6)==0) { if(phfn->FieldDataLength==12 && strncasecmp(phfn->FieldData,"100-Continue",12)==0) { // // We received an Expect: 100-Continue, so lets give it to em' !! // if(ILibAsyncSocket_Send(socketModule, "HTTP/1.1 100 Continue\r\n\r\n", 25, ILibAsyncSocket_MemoryOwnership_STATIC)==ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR) { // // Client decided to go away, so we'll just abort, // and pretend there is no body. // wcdo->WaitForClose=0; wcdo->BytesLeft = 0; wcdo->Chunked=0; break; } } } if(phfn->FieldLength==17 && strncasecmp(phfn->Field,"transfer-encoding",17)==0) { if(phfn->FieldDataLength==7 && strncasecmp(phfn->FieldData,"chunked",7)==0) { // // This packet was chunk encoded // wcdo->WaitForClose=0; wcdo->Chunked = 1; } } if(phfn->FieldLength==10 && strncasecmp(phfn->Field,"connection",10)==0) { if(phfn->FieldDataLength==5 && strncasecmp(phfn->FieldData,"close",5)==0) { // // This packet specified connection: close token // wcdo->ConnectionCloseSpecified=1; } }//{{{ <--REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT }}} if(phfn->FieldLength==14 && strncasecmp(phfn->Field,"content-length",14)==0) { // // This packet has a Content-Length // wcdo->WaitForClose=0; phfn->FieldData[phfn->FieldDataLength] = '\0'; wcdo->BytesLeft = atoi(phfn->FieldData); } phfn = phfn->NextField; }//{{{ REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT--> }}} if(atof(wcdo->header->Version)>1.0) { // // This is HTTP/1.1 or better, so we need to check to see // 1. Transfer-Encoding is not chunked // 2. Connection: close is NOT specified // 3. Content-Length is not specified // because than the packet MUST NOT have a body. // if(wcdo->Chunked==0 && wcdo->ConnectionCloseSpecified==0 && wcdo->WaitForClose!=0) { wcdo->WaitForClose=0; wcdo->BytesLeft=0; } } if(wcdo->header->StatusCode>=100 && wcdo->header->StatusCode<=199) { // HTTP Informational, ignore packet wcdo->FinHeader=0; *p_beginPointer += wcdo->HeaderLength; ILibDestructPacket(wcdo->header); wcdo->header = NULL; break; } else if(wcdo->header->StatusCode==204 || wcdo->header->StatusCode==304) { // No Body wcdo->WaitForClose=0; wcdo->BytesLeft = 0; }//{{{ <--REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT }}} if(wcdo->Server!=0 && wcdo->BytesLeft==-1 && wcdo->Chunked==0) { // // This request has no body // wcdo->BytesLeft=0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -