largepost.pp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. (* Feel free to use this example code in any way
  2. you see fit (Public Domain) *)
  3. // Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/largepost.c
  4. program largepost;
  5. {$mode objfpc}{$H+}
  6. uses
  7. libmicrohttpd, SysUtils, cutils;
  8. type
  9. TConnectionInfoStruct = record
  10. ConnectionType: cint;
  11. PostProcessor: PMHD_PostProcessor;
  12. Fp: FILEptr;
  13. AnswerString: Pcchar;
  14. AnswerCode: cint;
  15. end;
  16. PConnectionInfoStruct = ^TConnectionInfoStruct;
  17. const
  18. PORT = 8888;
  19. POSTBUFFERSIZE = 512;
  20. MAXCLIENTS = 2;
  21. GET = 0;
  22. POST = 1;
  23. var
  24. NrOfUploadingClients: Cardinal;
  25. AskPage: Pcchar =
  26. '<html><body>'+#10+
  27. 'Upload a file, please!<br>'+#10+
  28. 'There are %d clients uploading at the moment.<br>'+#10+
  29. '<form action="/filepost" method="post" enctype="multipart/form-data">'+#10+
  30. '<input name="file" type="file">'+#10+
  31. '<input type="submit" value="Send"></form>'+#10+
  32. '</body></html>';
  33. BusyPage: Pcchar = '<html><body>This server is busy, please try again later.</body></html>';
  34. CompletePage: Pcchar = '<html><body>The upload has been completed.</body></html>';
  35. ErrorPage: Pcchar = '<html><body>This doesn''t seem to be right.</body></html>';
  36. ServerErrorPage: Pcchar = '<html><body>An internal server error has occurred.</body></html>';
  37. FileExistsPage: Pcchar = '<html><body>This file already exists.</body></html>';
  38. function SendPage(AConnection: PMHD_Connection; APage: Pcchar; AStatusCode: cint): cint;
  39. var
  40. VRet: cint;
  41. VResponse: PMHD_Response;
  42. begin
  43. VResponse := MHD_create_response_from_buffer(Length(APage),
  44. Pointer(APage), MHD_RESPMEM_MUST_COPY);
  45. if not Assigned(VResponse) then
  46. Exit(MHD_NO);
  47. MHD_add_response_header(VResponse, MHD_HTTP_HEADER_CONTENT_TYPE, 'text/html');
  48. VRet := MHD_queue_response(AConnection, AStatusCode, VResponse);
  49. MHD_destroy_response(VResponse);
  50. Result := VRet;
  51. end;
  52. function IteratePost(AConInfoCls: Pointer; AKind: MHD_ValueKind; AKey: Pcchar;
  53. AFileName: Pcchar; AContentType: Pcchar; ATransferEncoding: Pcchar;
  54. AData: Pcchar; AOff: cuint64; ASize: size_t): cint; cdecl;
  55. var
  56. VConInfo: PConnectionInfoStruct;
  57. begin
  58. VConInfo := AConInfoCls;
  59. VConInfo^.AnswerString := ServerErrorPage;
  60. VConInfo^.AnswerCode := MHD_HTTP_INTERNAL_SERVER_ERROR;
  61. if StrComp(AKey, 'file') <> 0 then
  62. Exit(MHD_NO);
  63. if not Assigned(VConInfo^.Fp) then
  64. begin
  65. if FileExists(AFileName) then
  66. begin
  67. VConInfo^.AnswerString := FileExistsPage;
  68. VConInfo^.AnswerCode := MHD_HTTP_FORBIDDEN;
  69. Exit(MHD_NO);
  70. end;
  71. VConInfo^.Fp := fopen(AFileName, fappendwrite);
  72. if not Assigned(VConInfo^.Fp) then
  73. Exit(MHD_NO);
  74. end;
  75. if ASize > 0 then
  76. if fwrite(AData, ASize, SizeOf(AnsiChar), VConInfo^.Fp) = 0 then
  77. Exit(MHD_NO);
  78. VConInfo^.AnswerString := CompletePage;
  79. VConInfo^.AnswerCode := MHD_HTTP_OK;
  80. Result := MHD_YES;
  81. end;
  82. procedure RequestCompleted(ACls: Pointer; AConnection: PMHD_Connection;
  83. AConCls: PPointer; AToe: MHD_RequestTerminationCode); cdecl;
  84. var
  85. VConInfo: PConnectionInfoStruct;
  86. begin
  87. VConInfo := AConCls^;
  88. if not Assigned(VConInfo) then
  89. Exit;
  90. if VConInfo^.ConnectionType = POST then
  91. begin
  92. if Assigned(VConInfo^.PostProcessor) then
  93. begin
  94. MHD_destroy_post_processor(VConInfo^.PostProcessor);
  95. Dec(NrOfUploadingClients);
  96. end;
  97. if Assigned(VConInfo^.Fp) then
  98. fclose(VConInfo^.Fp);
  99. end;
  100. FreeMem(VConInfo);
  101. AConCls^ := nil;
  102. end;
  103. function AnswerToConnection(ACls: Pointer; AConnection: PMHD_Connection;
  104. AUrl: Pcchar; AMethod: Pcchar; AVersion: Pcchar; AUploadData: Pcchar;
  105. AUploadDataSize: Psize_t; AConCls: PPointer): cint; cdecl;
  106. var
  107. VBuffer: array[0..1024] of AnsiChar;
  108. VConInfo: PConnectionInfoStruct;
  109. begin
  110. if not Assigned(AConCls^) then
  111. begin
  112. if NrOfUploadingClients >= MAXCLIENTS then
  113. Exit(SendPage(AConnection, BusyPage, MHD_HTTP_SERVICE_UNAVAILABLE));
  114. VConInfo := AllocMem(SizeOf(TConnectionInfoStruct));
  115. if not Assigned(VConInfo) then
  116. Exit(MHD_NO);
  117. VConInfo^.Fp := nil;
  118. if StrComp(AMethod, 'POST') = 0 then
  119. begin
  120. VConInfo^.PostProcessor := MHD_create_post_processor(AConnection,
  121. POSTBUFFERSIZE, @IteratePost, VConInfo);
  122. if not Assigned(VConInfo^.PostProcessor) then
  123. begin
  124. FreeMem(VConInfo);
  125. Exit(MHD_NO);
  126. end;
  127. Inc(NrOfUploadingClients);
  128. VConInfo^.ConnectionType := POST;
  129. VConInfo^.AnswerCode := MHD_HTTP_OK;
  130. VConInfo^.AnswerString := CompletePage;
  131. end
  132. else
  133. VConInfo^.ConnectionType := GET;
  134. AConCls^ := VConInfo;
  135. Exit(MHD_YES);
  136. end;
  137. if StrComp(AMethod, 'GET') = 0 then
  138. begin
  139. StrLFmt(VBuffer, SizeOf(VBuffer), AskPage, [NrOfUploadingClients]);
  140. Exit(SendPage(AConnection, VBuffer, MHD_HTTP_OK));
  141. end;
  142. if StrComp(AMethod, 'POST') = 0 then
  143. begin
  144. VConInfo := AConCls^;
  145. if AUploadDataSize^ <> 0 then
  146. begin
  147. MHD_post_process(VConInfo^.PostProcessor, AUploadData, AUploadDataSize^);
  148. AUploadDataSize^ := 0;
  149. Exit(MHD_YES);
  150. end
  151. else
  152. begin
  153. if Assigned(VConInfo^.Fp) then
  154. begin
  155. fclose(VConInfo^.Fp);
  156. VConInfo^.Fp := nil;
  157. end;
  158. (* Now it is safe to open and inspect the file before calling send_page with a response *)
  159. Exit(SendPage(AConnection, VConInfo^.AnswerString, VConInfo^.AnswerCode));
  160. end;
  161. end;
  162. Result := SendPage(AConnection, ErrorPage, MHD_HTTP_BAD_REQUEST);
  163. end;
  164. var
  165. VDaemon: PMHD_Daemon;
  166. begin
  167. VDaemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, nil, nil,
  168. @AnswerToConnection, nil, MHD_OPTION_NOTIFY_COMPLETED, @RequestCompleted,
  169. nil, MHD_OPTION_END);
  170. if not Assigned(VDaemon) then
  171. Halt(1);
  172. ReadLn;
  173. MHD_stop_daemon(VDaemon);
  174. end.